Compare commits
61 Commits
pre-releas
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f11f6445ac | ||
|
|
ac1e01bfec | ||
|
|
dc60b03bcf | ||
|
|
0b85f35184 | ||
|
|
8e94c48e34 | ||
|
|
ff4f16e109 | ||
|
|
6b2eea6c18 | ||
|
|
1885161d67 | ||
|
|
7752d9465d | ||
|
|
f867ebfb33 | ||
|
|
a31398883e | ||
|
|
534443a475 | ||
|
|
c8292cf977 | ||
|
|
0447387079 | ||
|
|
27a22c2074 | ||
|
|
b3c6b5ae3a | ||
|
|
1aece69ad5 | ||
|
|
d6ca9d7273 | ||
|
|
abaaeff5d9 | ||
|
|
6c513aeb04 | ||
|
|
d5b88c7a01 | ||
|
|
011496349a | ||
|
|
e1fb23c112 | ||
|
|
3ddca10161 | ||
|
|
643bd9f103 | ||
|
|
9cda67c636 | ||
|
|
a1c158f8cd | ||
|
|
ee5ae888ce | ||
|
|
1ef79280fc | ||
|
|
05aea0a27b | ||
|
|
1ec37eae02 | ||
|
|
83d1d97e57 | ||
|
|
343e50a39d | ||
|
|
2ac1b425c7 | ||
|
|
5fcfd6ec02 | ||
|
|
57994f9100 | ||
|
|
61835326ef | ||
|
|
363a2baf17 | ||
|
|
80d3964320 | ||
|
|
451bca454c | ||
|
|
6826a9e5be | ||
|
|
a40885683c | ||
|
|
b33043f8b6 | ||
|
|
c2f44adc2e | ||
|
|
30880f8aa9 | ||
|
|
9124f617f5 | ||
|
|
cde86d8b6c | ||
|
|
642ca43cdc | ||
|
|
d503827ad0 | ||
|
|
fe69282d89 | ||
|
|
e3ae734150 | ||
|
|
5c15aa975d | ||
|
|
628c87facc | ||
|
|
816c32c899 | ||
|
|
fb1263043a | ||
|
|
fd44c23181 | ||
|
|
ae90a158bd | ||
|
|
5b4f9c32c4 | ||
|
|
d3907bb427 | ||
|
|
a60414b15c | ||
|
|
fd360c9995 |
1
.gitignore
vendored
@@ -153,3 +153,4 @@ cython_debug/
|
||||
|
||||
*.db
|
||||
*.zip
|
||||
*.db-journal
|
||||
|
||||
BIN
Image/1.png
|
Before Width: | Height: | Size: 320 KiB After Width: | Height: | Size: 224 KiB |
BIN
Image/10.png
Normal file
|
After Width: | Height: | Size: 131 KiB |
BIN
Image/11.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
Image/12.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
Image/13.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
Image/14.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
Image/15.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
Image/16.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
Image/17.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
Image/18.png
Normal file
|
After Width: | Height: | Size: 320 KiB |
BIN
Image/7.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
Image/8.png
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
Image/9.png
Normal file
|
After Width: | Height: | Size: 378 KiB |
BIN
Image/dashboard.png
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
Image/dashboard_new.png
Normal file
|
After Width: | Height: | Size: 195 KiB |
BIN
Image/group.png
|
Before Width: | Height: | Size: 593 KiB After Width: | Height: | Size: 94 KiB |
BIN
Image/wx.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
@@ -1,4 +1,4 @@
|
||||
# 检出阈值,越高越难检出但是也会越准确
|
||||
MAX_THREAT_SCORE = 170
|
||||
MAX_THREAT_SCORE = 45
|
||||
# 授权访问主站的IP列表.如果不在后台里面则不能访问后台
|
||||
ALLOW_ACCESS_IP = ['127.0.0.1']
|
||||
ALLOW_ACCESS_IP = ['127.0.0.1', '192.168.111.189', '192.168.111.187']
|
||||
|
||||
56
Server/hash_white_list.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import process
|
||||
import sql
|
||||
g_white_list = []
|
||||
g_white_dll_load_list = [
|
||||
'c:\\windows\\system32\\advapi32.dll',
|
||||
'c:\\windows\\system32\\crypt32.dll',
|
||||
'c:\\windows\\system32\\cryptdll.dll',
|
||||
'c:\\windows\\system32\\gdi32.dll',
|
||||
'c:\\windows\\system32\\imm32.dll',
|
||||
'c:\\windows\\system32\\kernel32.dll',
|
||||
'c:\\windows\\system32\\kernelbase.dll',
|
||||
'c:\\windows\\system32\\msasn1.dll',
|
||||
'c:\\windows\\system32\\msvcrt.dll',
|
||||
'c:\\windows\\system32\\ntdll.dll',
|
||||
'c:\\windows\\system32\\rpcrt4.dll',
|
||||
'c:\\windows\\system32\\rsaenh.dll',
|
||||
'c:\\windows\\system32\\samlib.dll',
|
||||
'c:\\windows\\system32\\sechost.dll',
|
||||
'c:\\windows\\system32\\secur32.dll',
|
||||
'c:\\windows\\system32\\shell32.dll',
|
||||
'c:\\windows\\system32\\shlwapi.dll',
|
||||
'c:\\windows\\system32\\sspicli.dll',
|
||||
'c:\\windows\\system32\\user32.dll',
|
||||
'c:\\windows\\system32\\vaultcli.dll',
|
||||
]
|
||||
|
||||
|
||||
def check_in_while_list(process: process.Process):
|
||||
parent_process = process.parent_process
|
||||
is_white = process.is_white or process.chain.root_process.is_white or process.parent_process.is_white
|
||||
if is_white == False:
|
||||
while parent_process:
|
||||
if parent_process is None:
|
||||
break
|
||||
if parent_process.is_white:
|
||||
is_white = True
|
||||
break
|
||||
if parent_process == process.chain.root_process:
|
||||
break
|
||||
parent_process = parent_process.parent_process
|
||||
return is_white
|
||||
|
||||
|
||||
def add_white_list(path, hash, reason):
|
||||
global g_white_list
|
||||
if hash in g_white_list:
|
||||
return False
|
||||
g_white_list.append(hash)
|
||||
sql.push_white_list(path, hash, reason)
|
||||
|
||||
|
||||
def synchronization_white_list():
|
||||
sql_data = sql.query_all_white_list()
|
||||
for data in sql_data:
|
||||
g_white_list.append(data[1])
|
||||
print("sync white list success, size: {}".format(len(sql_data)))
|
||||
379
Server/log.py
@@ -7,112 +7,73 @@ import sql
|
||||
import global_vars
|
||||
import config
|
||||
import plugin
|
||||
import hash_white_list
|
||||
|
||||
LOG_TYPE_PROCESS_CREATE = 1
|
||||
LOG_TYPE_PROCESS_ACTION = 2
|
||||
|
||||
|
||||
def process_log(host, json_log, raw_log):
|
||||
log = json_log["data"]
|
||||
def update_att_ck(process: process.Process, score, hit_name, attck_t_list):
|
||||
if hash_white_list.check_in_while_list(process):
|
||||
score = 0
|
||||
for t in attck_t_list:
|
||||
process.set_attck(score, t, hit_name)
|
||||
# 更新命中的规则
|
||||
return global_vars.THREAT_TYPE_PROCESS
|
||||
|
||||
|
||||
def update_threat(process: process.Process, score, rule_hit_name):
|
||||
had_threat = global_vars.THREAT_TYPE_NONE
|
||||
current_process: process.Process = None
|
||||
rule_hit_name = ""
|
||||
score = 0
|
||||
chain_hash = ""
|
||||
params = ""
|
||||
user = ""
|
||||
if hash_white_list.check_in_while_list(process):
|
||||
score = 0
|
||||
if score > 0:
|
||||
# 更新命中的规则
|
||||
process.set_score(score, rule_hit_name)
|
||||
had_threat = global_vars.THREAT_TYPE_PROCESS
|
||||
return had_threat
|
||||
|
||||
if json_log["action"] == "processcreate":
|
||||
pid = log["processid"]
|
||||
ppid = log["parentprocessid"]
|
||||
path = log["image"]
|
||||
params = log["commandline"]
|
||||
user = log["user"]
|
||||
hash = log["hashes"].split(",")[0].split("=")[1]
|
||||
|
||||
parent_pid = log["parentprocessid"]
|
||||
parent_ppid = parent_pid
|
||||
parent_path = log["parentimage"]
|
||||
parent_params = log["parentcommandline"]
|
||||
parent_user = log["parentuser"]
|
||||
create_time = int(round(time.time() * 1000))
|
||||
def match_threat(process: process.Process, log, log_type):
|
||||
had_threat = global_vars.THREAT_TYPE_NONE
|
||||
success_match = False
|
||||
hit_name = ''
|
||||
hit_score = 0
|
||||
is_ioa = False
|
||||
if log_type == LOG_TYPE_PROCESS_CREATE:
|
||||
success_match, is_ioa, attck_t_list, hit_score, rule_hit_name = rule.calc_score_in_create_process(
|
||||
log)
|
||||
elif log_type == LOG_TYPE_PROCESS_ACTION:
|
||||
success_match, is_ioa, attck_t_list, hit_score, rule_hit_name = rule.calc_score_in_action(
|
||||
log)
|
||||
if success_match == False:
|
||||
return had_threat, is_ioa, hit_name, hit_score
|
||||
# 匹配到了首先更新att&ck的t
|
||||
had_threat = update_att_ck(
|
||||
process, hit_score, rule_hit_name, attck_t_list)
|
||||
hit_name = rule_hit_name
|
||||
if is_ioa:
|
||||
had_threat = update_threat(
|
||||
process, hit_score, rule_hit_name)
|
||||
else:
|
||||
is_match_software, software_name, software_score = rule.match_att_ck_software(
|
||||
process.chain.attck_hit_list)
|
||||
if is_match_software:
|
||||
# 匹配到software了,设置为ioa
|
||||
had_threat = update_threat(
|
||||
process, software_score, software_name)
|
||||
hit_name = software_name
|
||||
hit_score = software_score
|
||||
#print('match_threat', process.path, is_ioa, hit_name, hit_score)
|
||||
# if had_threat != global_vars.THREAT_TYPE_NONE:
|
||||
# print('path: {} hit_name: {} socre: {}'.format(
|
||||
# process.path, hit_name, hit_score))
|
||||
return had_threat, is_ioa, hit_name, hit_score
|
||||
|
||||
if path in process.skip_process_path or path in process.skip_process_path:
|
||||
return
|
||||
parent_process: process.Process = process.get_process_by_pid(ppid)
|
||||
score, rule_hit_name = rule.calc_score_in_create_process(log)
|
||||
if hash in process.skip_md5:
|
||||
return
|
||||
if parent_process is None or parent_path in process.root_process_path:
|
||||
# build a process
|
||||
parent_process = process.Process(
|
||||
parent_pid,
|
||||
parent_ppid,
|
||||
parent_path,
|
||||
parent_params,
|
||||
create_time - 1,
|
||||
"None",
|
||||
parent_user,
|
||||
host,
|
||||
)
|
||||
child = process.Process(
|
||||
pid, ppid, path, params, create_time, hash, parent_user, host
|
||||
)
|
||||
chain = process.create_chain(parent_process)
|
||||
chain.add_process(child, parent_pid)
|
||||
current_process = child
|
||||
if score > 0:
|
||||
child.set_score(score, rule_hit_name)
|
||||
had_threat = global_vars.THREAT_TYPE_PROCESS
|
||||
else:
|
||||
child = process.Process(
|
||||
pid, ppid, path, params, create_time, hash, user, host
|
||||
)
|
||||
parent_process.chain.add_process(child, ppid)
|
||||
current_process = child
|
||||
if score > 0:
|
||||
child.set_score(score, rule_hit_name)
|
||||
had_threat = global_vars.THREAT_TYPE_PROCESS
|
||||
|
||||
had_threat_plugin = plugin.dispath_rule_new_process_create(
|
||||
host, current_process, raw_log, json_log
|
||||
)
|
||||
if had_threat == global_vars.THREAT_TYPE_NONE:
|
||||
had_threat = had_threat_plugin
|
||||
elif json_log["action"] == "processterminal":
|
||||
pid = log["processid"]
|
||||
current_process = process.get_process_by_pid(pid)
|
||||
if current_process is not None:
|
||||
plugin.dispath_process_terminal(host, current_process, raw_log, json_log)
|
||||
current_process.active = False
|
||||
current_process.chain.terminate_count += 1
|
||||
if current_process.chain.terminate_count >= (
|
||||
current_process.chain.active_count - 1
|
||||
):
|
||||
current_process.chain.active = False
|
||||
if current_process.chain.risk_score >= config.MAX_THREAT_SCORE:
|
||||
sql.update_threat_log(
|
||||
host,
|
||||
current_process.chain.risk_score,
|
||||
json.dumps(current_process.chain.operationlist),
|
||||
current_process.chain.hash,
|
||||
current_process.chain.get_json(),
|
||||
global_vars.THREAT_TYPE_PROCESS,
|
||||
True,
|
||||
)
|
||||
process.g_ProcessChainList.remove(current_process.chain)
|
||||
elif "processid" in log:
|
||||
current_process = process.get_process_by_pid(log["processid"])
|
||||
if current_process is not None:
|
||||
log["action"] = json_log["action"]
|
||||
score, rule_hit_name = rule.calc_score_in_action(log)
|
||||
if score > 0:
|
||||
current_process.set_score(score, rule_hit_name)
|
||||
had_threat = global_vars.THREAT_TYPE_PROCESS
|
||||
had_threat_plugin = plugin.dispath_rule_new_process_action(
|
||||
host, current_process, raw_log, json_log
|
||||
)
|
||||
if had_threat == global_vars.THREAT_TYPE_NONE:
|
||||
had_threat = had_threat_plugin
|
||||
|
||||
def update_process_threat_status(current_process: process.Process, host, had_threat):
|
||||
if current_process is not None:
|
||||
# if current_process.path.find("f.exe") != -1:
|
||||
# print(log)
|
||||
if current_process.chain.risk_score >= config.MAX_THREAT_SCORE:
|
||||
if had_threat == global_vars.THREAT_TYPE_PROCESS:
|
||||
current_process.chain.update_process_tree()
|
||||
@@ -137,6 +98,7 @@ def process_log(host, json_log, raw_log):
|
||||
host,
|
||||
current_process.chain.risk_score,
|
||||
json.dumps(current_process.chain.operationlist),
|
||||
json.dumps(current_process.chain.attck_hit_list),
|
||||
current_process.chain.hash,
|
||||
current_process.chain.get_json(),
|
||||
global_vars.THREAT_TYPE_PROCESS,
|
||||
@@ -147,11 +109,123 @@ def process_log(host, json_log, raw_log):
|
||||
host,
|
||||
current_process.chain.risk_score,
|
||||
json.dumps(current_process.chain.operationlist),
|
||||
json.dumps(current_process.chain.attck_hit_list),
|
||||
current_process.chain.hash,
|
||||
current_process.chain.get_json(),
|
||||
global_vars.THREAT_TYPE_PROCESS,
|
||||
current_process.chain.active == False,
|
||||
)
|
||||
|
||||
|
||||
def process_log(host, json_log, raw_log):
|
||||
log = json_log["data"]
|
||||
had_threat = global_vars.THREAT_TYPE_NONE
|
||||
current_process: process.Process = None
|
||||
rule_hit_name = ""
|
||||
score = 0
|
||||
chain_hash = ""
|
||||
params = ""
|
||||
user = ""
|
||||
is_ioa = False
|
||||
|
||||
if json_log["action"] == "processcreate":
|
||||
pid = log["processid"]
|
||||
ppid = log["parentprocessid"]
|
||||
path = log["image"]
|
||||
params = log["commandline"]
|
||||
user = log["user"]
|
||||
hash = log["hashes"].split(",")[0].split("=")[1]
|
||||
|
||||
parent_pid = log["parentprocessid"]
|
||||
parent_ppid = parent_pid
|
||||
parent_path = log["parentimage"]
|
||||
parent_params = log["parentcommandline"]
|
||||
parent_user = log["parentuser"]
|
||||
create_time = int(round(time.time() * 1000))
|
||||
|
||||
if path in process.skip_process_path or path in process.skip_process_path:
|
||||
return
|
||||
parent_process: process.Process = process.get_process_by_pid(ppid)
|
||||
|
||||
if hash in process.skip_md5:
|
||||
return
|
||||
if parent_process is None or parent_path in process.root_process_path:
|
||||
# build a process
|
||||
parent_process = process.Process(
|
||||
parent_pid,
|
||||
parent_ppid,
|
||||
parent_path,
|
||||
parent_params,
|
||||
create_time - 1,
|
||||
"None",
|
||||
parent_user,
|
||||
host,
|
||||
)
|
||||
is_white_list = hash in hash_white_list.g_white_list
|
||||
child = process.Process(
|
||||
pid, ppid, path, params, create_time, hash, parent_user, host, is_white_list
|
||||
)
|
||||
parent_process.parent_process = parent_process
|
||||
child.parent_process = parent_process
|
||||
chain = process.create_chain(parent_process)
|
||||
chain.add_process(child, parent_pid)
|
||||
current_process = child
|
||||
|
||||
had_threat, is_ioa, rule_hit_name, score = match_threat(
|
||||
current_process, log, LOG_TYPE_PROCESS_CREATE)
|
||||
else:
|
||||
is_white_list = hash in hash_white_list.g_white_list
|
||||
child = process.Process(
|
||||
pid, ppid, path, params, create_time, hash, user, host, is_white_list
|
||||
)
|
||||
child.parent_process = parent_process
|
||||
parent_process.chain.add_process(child, ppid)
|
||||
current_process = child
|
||||
|
||||
had_threat, is_ioa, rule_hit_name, score = match_threat(
|
||||
current_process, log, LOG_TYPE_PROCESS_CREATE)
|
||||
|
||||
had_threat_plugin = plugin.dispath_rule_new_process_create(
|
||||
host, current_process, raw_log, json_log
|
||||
)
|
||||
if had_threat == global_vars.THREAT_TYPE_NONE:
|
||||
had_threat = had_threat_plugin
|
||||
elif json_log["action"] == "processterminal":
|
||||
pid = log["processid"]
|
||||
current_process = process.get_process_by_pid(pid)
|
||||
if current_process is not None:
|
||||
plugin.dispath_process_terminal(
|
||||
host, current_process, raw_log, json_log)
|
||||
current_process.active = False
|
||||
current_process.chain.terminate_count += 1
|
||||
if current_process.chain.terminate_count >= (
|
||||
current_process.chain.active_count - 1
|
||||
):
|
||||
current_process.chain.active = False
|
||||
if current_process.chain.risk_score >= config.MAX_THREAT_SCORE:
|
||||
sql.update_threat_log(
|
||||
host,
|
||||
current_process.chain.risk_score,
|
||||
json.dumps(current_process.chain.operationlist),
|
||||
json.dumps(current_process.chain.attck_hit_list),
|
||||
current_process.chain.hash,
|
||||
current_process.chain.get_json(),
|
||||
global_vars.THREAT_TYPE_PROCESS,
|
||||
True,
|
||||
)
|
||||
process.g_ProcessChainList.remove(current_process.chain)
|
||||
elif "processid" in log:
|
||||
current_process = process.get_process_by_pid(log["processid"])
|
||||
if current_process is not None:
|
||||
log["action"] = json_log["action"]
|
||||
had_threat, is_ioa, rule_hit_name, score = match_threat(
|
||||
current_process, log, LOG_TYPE_PROCESS_ACTION)
|
||||
had_threat_plugin = plugin.dispath_rule_new_process_action(
|
||||
host, current_process, raw_log, json_log
|
||||
)
|
||||
if had_threat == global_vars.THREAT_TYPE_NONE:
|
||||
had_threat = had_threat_plugin
|
||||
update_process_threat_status(current_process, host, had_threat)
|
||||
parent_pid = 0
|
||||
target_pid = 0
|
||||
self_hash = ""
|
||||
@@ -170,6 +244,12 @@ def process_log(host, json_log, raw_log):
|
||||
target_image_path = target_process.path
|
||||
target_hash = target_process.md5
|
||||
self_hash = current_process.md5
|
||||
# 以后有其他排除需求再优化
|
||||
# if json_log['action'] == 'imageload' and (json_log['data']['imageloaded'][len(json_log['data']['imageloaded']) - 4:] == '.exe' or json_log['data']['imageloaded'] in hash_white_list.g_white_dll_load_list):
|
||||
# return
|
||||
|
||||
if json_log['action'] == 'imageload':
|
||||
return
|
||||
|
||||
sql.push_process_raw(
|
||||
host,
|
||||
@@ -186,10 +266,107 @@ def process_log(host, json_log, raw_log):
|
||||
params,
|
||||
user,
|
||||
)
|
||||
|
||||
"""
|
||||
'''
|
||||
for iter in process.g_ProcessChainList:
|
||||
item: process.Process = iter
|
||||
if item.risk_score >= config.MAX_THREAT_SCORE:
|
||||
item.print_process()
|
||||
"""
|
||||
'''
|
||||
|
||||
|
||||
def process_raw_log(raw_logs: list) -> list:
|
||||
return_data = []
|
||||
process_chain_list = []
|
||||
|
||||
raw_logs.sort(key=operator.attrgetter("timestamp"))
|
||||
|
||||
def _get_process_chain(pid, host: str) -> process.ProcessChain:
|
||||
for iter in process_chain_list:
|
||||
chain_item: process.ProcessChain = iter
|
||||
if chain_item.host != host:
|
||||
continue
|
||||
process_item = chain_item.find_process_by_pid(pid)
|
||||
if process_item is not None:
|
||||
return chain_item
|
||||
return None
|
||||
|
||||
for log in raw_logs:
|
||||
log: sql.raw_process_log = log
|
||||
pid = log.pid
|
||||
ppid = log.ppid
|
||||
path = log.path
|
||||
params = log.commandline
|
||||
user = log.user
|
||||
hash = log.hash
|
||||
create_time = log.timestamp
|
||||
host = log.host
|
||||
current_process: process.Process = None
|
||||
if path in process.skip_process_path:
|
||||
continue
|
||||
if log.action.lower() == "processcreate":
|
||||
|
||||
chain = _get_process_chain(pid, host)
|
||||
if chain is not None:
|
||||
parent_process = chain.find_process_by_pid(ppid)
|
||||
else:
|
||||
parent_process = None
|
||||
|
||||
if chain is None:
|
||||
# build a process chain
|
||||
current_process = process.Process(
|
||||
pid, ppid, path, params, create_time, hash, user, host
|
||||
)
|
||||
chain = process.create_chain(current_process)
|
||||
process_chain_list.append(chain)
|
||||
else:
|
||||
current_process = process.Process(
|
||||
pid, ppid, path, params, create_time, hash, user, host
|
||||
)
|
||||
chain.add_process(current_process, ppid)
|
||||
elif log.action.lower() == "processterminal":
|
||||
chain = _get_process_chain(pid, host)
|
||||
if chain is not None:
|
||||
current_process = chain.find_process_by_pid(pid)
|
||||
current_process.active = False
|
||||
current_process.chain.terminate_count += 1
|
||||
if (
|
||||
current_process.chain.terminate_count
|
||||
>= current_process.chain.active_count
|
||||
):
|
||||
current_process.chain.active = False
|
||||
else:
|
||||
# 不在指定时段内被创建的进程的结束事件
|
||||
continue
|
||||
else:
|
||||
chain = _get_process_chain(pid, host)
|
||||
if chain is None:
|
||||
continue
|
||||
current_process = chain.find_process_by_pid(pid)
|
||||
if current_process is None:
|
||||
continue
|
||||
|
||||
# if current_process is None :
|
||||
# breakpoint()
|
||||
start_process = current_process.chain.root_process
|
||||
start_process_info = {
|
||||
"path": start_process.path,
|
||||
"hash": start_process.md5,
|
||||
"params": start_process.params,
|
||||
"user": start_process.user,
|
||||
"create_time": start_process.time,
|
||||
}
|
||||
return_data.append(
|
||||
{
|
||||
"host": current_process.host,
|
||||
"chain_hash": current_process.chain.hash,
|
||||
"hit_rule": log.hit,
|
||||
"time": log.timestamp,
|
||||
"type": log.type,
|
||||
"risk_score": log.score,
|
||||
"id": log.id,
|
||||
"is_end": current_process.chain.active == False,
|
||||
"start_process": start_process_info,
|
||||
}
|
||||
)
|
||||
|
||||
return return_data
|
||||
|
||||
@@ -97,15 +97,15 @@ def dispath_html_menu():
|
||||
plugin_menu = []
|
||||
for index in range(len(global_vars.g_plugs)):
|
||||
_, plug_obj = global_vars.g_plugs[index]
|
||||
if hasattr(plug_obj, "html_menu"):
|
||||
plugin_menu.append(plug_obj.html_menu())
|
||||
if hasattr(plug_obj, "html_menu"):
|
||||
plugin_menu.append(plug_obj.html_menu())
|
||||
return plugin_menu
|
||||
|
||||
|
||||
def dispath_html_draw(name):
|
||||
for index in range(len(global_vars.g_plugs)):
|
||||
_, plug_obj = global_vars.g_plugs[index]
|
||||
if hasattr(plug_obj, "html_draw"):
|
||||
if plug_obj.rm_plugs_config['html'] == name:
|
||||
return plug_obj.html_draw()
|
||||
if hasattr(plug_obj, "html_draw"):
|
||||
if plug_obj.rm_plugs_config['html'] == name:
|
||||
return plug_obj.html_draw()
|
||||
return 'Access Denied '
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import global_vars
|
||||
import yara
|
||||
import glob
|
||||
from pathlib import Path
|
||||
#import yara
|
||||
|
||||
rm_plugs_config = {
|
||||
"enable": False,
|
||||
|
||||
342
Server/plugins/ioc_opswat/opswat.py
Normal file
@@ -0,0 +1,342 @@
|
||||
import requests
|
||||
import global_vars
|
||||
import process
|
||||
import hash_white_list
|
||||
from threading import Thread
|
||||
# 引入sqlalchemy中相关模块
|
||||
from sqlalchemy import Column, Integer, String
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
import time
|
||||
from sqlalchemy import create_engine, MetaData, Table
|
||||
|
||||
STATUS_CLEAN = -1
|
||||
STATUS_UNK = 0
|
||||
STATUS_VIRUS = 1
|
||||
|
||||
# 自己去https://metadefender.opswat.com/注册一个申请一个免费的api
|
||||
# 我这边的api有每日最大使用限制!!!!!!用的人多了很有可能会被封掉
|
||||
rm_plugs_config = {
|
||||
"enable": True,
|
||||
"author": "huoji",
|
||||
"description": "opswat ioc检测扩展插件",
|
||||
"version": "0.0.1",
|
||||
# !自己去https://metadefender.opswat.com/注册一个申请一个免费的api!
|
||||
# !自己去https://metadefender.opswat.com/注册一个申请一个免费的api!
|
||||
# !自己去https://metadefender.opswat.com/注册一个申请一个免费的api!
|
||||
"apikey": "010d4868aef799750e2828fdf17a4d98",
|
||||
}
|
||||
g_engine = None
|
||||
g_opswat_cache_hashes_table = None
|
||||
g_opswat_cache_hashes_ins = None
|
||||
g_opswat_cache_ip_addr_table = None
|
||||
g_opswat_cache_ip_addr_ins = None
|
||||
g_sql_base = declarative_base()
|
||||
g_check_hashes_list = {}
|
||||
g_check_ip_list = {}
|
||||
|
||||
|
||||
class opswat_cache_hashes(g_sql_base):
|
||||
__tablename__ = "opswat_cache_hashs"
|
||||
# 定义各字段
|
||||
id = Column(Integer, primary_key=True)
|
||||
# 主机ip
|
||||
host = Column(String)
|
||||
# 进程路径
|
||||
path = Column(String)
|
||||
# hash
|
||||
hash = Column(String)
|
||||
# 时间戳
|
||||
timestamp = Column(Integer)
|
||||
# 信息 -1绿色 0 未知 1病毒
|
||||
status = Column(Integer)
|
||||
|
||||
def __str__(self):
|
||||
return self.id
|
||||
|
||||
|
||||
class opswat_cache_ip_addr(g_sql_base):
|
||||
__tablename__ = "opswat_cache_ip_addr"
|
||||
# 定义各字段
|
||||
id = Column(Integer, primary_key=True)
|
||||
# 主机ip
|
||||
host = Column(String)
|
||||
# 进程路径
|
||||
path = Column(String)
|
||||
# ip_addr
|
||||
ip_addr = Column(String)
|
||||
# 时间戳
|
||||
timestamp = Column(Integer)
|
||||
# 信息 -1绿色 0 未知 1病毒
|
||||
status = Column(Integer)
|
||||
|
||||
def __str__(self):
|
||||
return self.id
|
||||
|
||||
|
||||
def search_ip_in_opswat(ip_addr):
|
||||
request_obj = requests.Session()
|
||||
request_obj.trust_env = False
|
||||
url = "https://api.metadefender.com/v4/ip/" + ip_addr
|
||||
headers = {
|
||||
"apikey": rm_plugs_config['apikey'],
|
||||
}
|
||||
status = STATUS_UNK
|
||||
try:
|
||||
response = request_obj.get(
|
||||
url, headers=headers, timeout=30, verify=True)
|
||||
if response.status_code == 200:
|
||||
json_data = response.json()
|
||||
if 'lookup_results' in json_data:
|
||||
if json_data['lookup_results']['detected_by'] >= 1:
|
||||
status = STATUS_VIRUS
|
||||
else:
|
||||
status = STATUS_CLEAN
|
||||
except:
|
||||
pass
|
||||
return status
|
||||
|
||||
|
||||
def search_hash_in_opswat(hash):
|
||||
request_obj = requests.Session()
|
||||
request_obj.trust_env = False
|
||||
url = "https://api.metadefender.com/v4/hash/" + hash
|
||||
headers = {
|
||||
"apikey": rm_plugs_config['apikey'],
|
||||
}
|
||||
status = STATUS_UNK
|
||||
try:
|
||||
response = request_obj.get(
|
||||
url, headers=headers, timeout=30, verify=True)
|
||||
if response.status_code == 200:
|
||||
json_data = response.json()
|
||||
if 'scan_all_result_i' in json_data['scan_results']:
|
||||
if json_data['scan_results']['total_detected_avs'] > 5:
|
||||
status = STATUS_VIRUS
|
||||
else:
|
||||
status = STATUS_CLEAN
|
||||
except:
|
||||
pass
|
||||
return status
|
||||
|
||||
|
||||
def async_call(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
Thread(target=fn, args=args, kwargs=kwargs).start()
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def query_hash(pHash):
|
||||
global g_opswat_cache_hashes_table
|
||||
sql_session = sessionmaker(bind=g_engine)
|
||||
hash_info = sql_session().query(
|
||||
g_opswat_cache_hashes_table).filter_by(hash=pHash).first()
|
||||
sql_session().close()
|
||||
if hash_info is None:
|
||||
return False, None
|
||||
last_time = hash_info[4]
|
||||
status = hash_info[5]
|
||||
is_need_update = False
|
||||
# 10 day
|
||||
if time.time() - last_time > 864000:
|
||||
is_need_update = True
|
||||
return is_need_update, status
|
||||
|
||||
|
||||
def query_ipaddr(pIp):
|
||||
global g_opswat_cache_ip_addr_table
|
||||
sql_session = sessionmaker(bind=g_engine)
|
||||
ip_info = sql_session().query(
|
||||
g_opswat_cache_ip_addr_table).filter_by(ip_addr=pIp).first()
|
||||
sql_session().close()
|
||||
if ip_info is None:
|
||||
return False, None
|
||||
last_time = ip_info[4]
|
||||
status = ip_info[5]
|
||||
is_need_update = False
|
||||
# 10 day
|
||||
if time.time() - last_time > 864000:
|
||||
is_need_update = True
|
||||
return is_need_update, status
|
||||
|
||||
|
||||
def update_ip_addr(ip_addr, net_status):
|
||||
global g_opswat_cache_ip_addr_table
|
||||
global g_engine
|
||||
conn = g_engine.connect()
|
||||
update = (
|
||||
g_opswat_cache_ip_addr_table.update()
|
||||
.values(status=net_status,
|
||||
timestamp=int(round(time.time() * 1000)))
|
||||
.where(g_opswat_cache_ip_addr_table.c.ip_addr == ip_addr)
|
||||
)
|
||||
result = conn.execute(update)
|
||||
conn.close()
|
||||
return result
|
||||
|
||||
|
||||
def update_hash(hash, new_status):
|
||||
global g_opswat_cache_hashes_table
|
||||
global g_engine
|
||||
conn = g_engine.connect()
|
||||
update = (
|
||||
g_opswat_cache_hashes_table.update()
|
||||
.values(
|
||||
status=new_status,
|
||||
timestamp=int(round(time.time() * 1000))
|
||||
)
|
||||
.where(
|
||||
g_opswat_cache_hashes_table.columns.hash == hash
|
||||
)
|
||||
)
|
||||
result = conn.execute(update)
|
||||
conn.close()
|
||||
return result
|
||||
|
||||
|
||||
def push_ip_addr(host, path, ip_addr, status):
|
||||
global g_opswat_cache_ip_addr_table
|
||||
global g_engine
|
||||
conn = g_engine.connect()
|
||||
insert = g_opswat_cache_ip_addr_table.insert().values(
|
||||
host=host,
|
||||
path=path,
|
||||
ip_addr=ip_addr,
|
||||
status=status,
|
||||
timestamp=int(round(time.time() * 1000))
|
||||
)
|
||||
result = conn.execute(insert)
|
||||
conn.close()
|
||||
return result
|
||||
|
||||
|
||||
def push_hash(
|
||||
host,
|
||||
path,
|
||||
hash,
|
||||
status
|
||||
):
|
||||
global g_engine
|
||||
global g_opswat_cache_hashes_table
|
||||
global g_opswat_cache_hashes_ins
|
||||
ins = g_opswat_cache_hashes_ins.values(
|
||||
host=host,
|
||||
path=path,
|
||||
hash=hash,
|
||||
status=status,
|
||||
timestamp=int(round(time.time() * 1000))
|
||||
)
|
||||
# 连接引擎
|
||||
conn = g_engine.connect()
|
||||
# 执行语句
|
||||
result = conn.execute(ins)
|
||||
conn.close()
|
||||
# print(raw_json)
|
||||
return result
|
||||
|
||||
|
||||
@async_call
|
||||
def asnyc_check_ip(current_process: process.Process, host, ip):
|
||||
global g_check_ip_list
|
||||
if ip in g_check_ip_list and g_check_ip_list[ip] != -2:
|
||||
return g_check_ip_list[ip]
|
||||
g_check_ip_list[ip] = STATUS_UNK
|
||||
cache_need_update, cache_status = query_ipaddr(ip)
|
||||
if cache_need_update or cache_status is None:
|
||||
create_one = False
|
||||
if cache_status is None:
|
||||
create_one = True
|
||||
cache_status = search_ip_in_opswat(ip)
|
||||
if create_one:
|
||||
push_ip_addr(host, current_process.path, ip, cache_status)
|
||||
else:
|
||||
push_ip_addr(ip, cache_status)
|
||||
|
||||
if cache_status == STATUS_VIRUS:
|
||||
current_process.set_score(666, "恶意网络链接IP:{}".format(ip))
|
||||
elif cache_status == STATUS_UNK:
|
||||
# crowdstrike: 这个我熟
|
||||
current_process.set_score(10, "低信誉ip链接:{}".format(ip))
|
||||
g_check_ip_list[ip] = cache_status
|
||||
|
||||
|
||||
@async_call
|
||||
def asnyc_check_domian(current_process: process.Process, host, domain):
|
||||
pass
|
||||
|
||||
|
||||
@async_call
|
||||
def asnyc_check_hash(current_process: process.Process, host):
|
||||
global g_check_hashes_list
|
||||
hash = current_process.md5
|
||||
if hash in g_check_hashes_list and g_check_hashes_list[hash] != -2:
|
||||
return g_check_hashes_list[hash]
|
||||
g_check_hashes_list[hash] = STATUS_UNK
|
||||
cache_need_update, cache_status = query_hash(hash)
|
||||
if cache_need_update or cache_status is None:
|
||||
create_one = False
|
||||
if cache_status is None:
|
||||
create_one = True
|
||||
cache_status = search_hash_in_opswat(hash)
|
||||
if create_one:
|
||||
push_hash(host, current_process.path, hash, cache_status)
|
||||
else:
|
||||
update_hash(hash, cache_status)
|
||||
|
||||
if cache_status == STATUS_VIRUS:
|
||||
current_process.set_score(666, "恶意软件")
|
||||
elif cache_status == STATUS_UNK:
|
||||
# crowdstrike: 这个我熟
|
||||
current_process.set_score(10, "低信誉文件")
|
||||
g_check_hashes_list[hash] = cache_status
|
||||
|
||||
|
||||
def rule_new_process_create(current_process: process.Process, host, raw_log_data, json_log_data):
|
||||
global g_check_hashes_list
|
||||
if rm_plugs_config['apikey'] != "" is not None and hash_white_list.check_in_while_list(current_process) == False:
|
||||
g_check_hashes_list[current_process.md5] = -2
|
||||
asnyc_check_hash(current_process, host)
|
||||
return global_vars.THREAT_TYPE_NONE
|
||||
|
||||
|
||||
def rule_new_process_action(current_process: process.Process, host, raw_log_data, json_log_data):
|
||||
global g_check_ip_list
|
||||
if rm_plugs_config['apikey'] != "" is not None and json_log_data['action'] == 'networkconnect' and hash_white_list.check_in_while_list(current_process) == False:
|
||||
# print('network connect{}'.format(
|
||||
# json_log_data['data']['destinationip']))
|
||||
ip_addr = json_log_data['data']['destinationip']
|
||||
if len(ip_addr) >= 5:
|
||||
g_check_ip_list[json_log_data['data']['destinationip']] = -2
|
||||
asnyc_check_ip(current_process, host,
|
||||
json_log_data['data']['destinationip'])
|
||||
return global_vars.THREAT_TYPE_NONE
|
||||
|
||||
|
||||
def rule_init():
|
||||
pass
|
||||
|
||||
|
||||
def plugin_init():
|
||||
global g_engine
|
||||
global g_metadata
|
||||
global g_sql_base
|
||||
global g_opswat_cache_hashes_table
|
||||
global g_opswat_cache_hashes_ins
|
||||
global g_opswat_cache_ip_addr_table
|
||||
global g_opswat_cache_ip_addr_ins
|
||||
print('opswat ioc检测扩展插件 2022/9/23 by huoji')
|
||||
|
||||
if rm_plugs_config['apikey'] != "":
|
||||
g_engine = create_engine(
|
||||
"sqlite:///plugin_opswat_cache.db?check_same_thread=False", echo=False)
|
||||
g_sql_base.metadata.create_all(g_engine)
|
||||
g_metadata = MetaData(g_engine)
|
||||
g_opswat_cache_hashes_table = Table(
|
||||
"opswat_cache_hashs", g_metadata, autoload=True)
|
||||
g_opswat_cache_hashes_ins = g_opswat_cache_hashes_table.insert()
|
||||
g_opswat_cache_ip_addr_table = Table(
|
||||
"opswat_cache_ip_addr", g_metadata, autoload=True)
|
||||
g_opswat_cache_ip_addr_ins = g_opswat_cache_ip_addr_table.insert()
|
||||
else:
|
||||
print('opswat ioc检测扩展插件未配置apikey,自己去metadefender.opswat.com申请一个!')
|
||||
61
Server/plugins/mimikazt_detect/mimikatz_detect.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import global_vars
|
||||
import process
|
||||
import hash_white_list
|
||||
rm_plugs_config = {
|
||||
"enable": True,
|
||||
"author": "huoji",
|
||||
"description": "检测mimikatz",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
||||
mimikatz_dll_list = [
|
||||
'c:\\windows\\system32\\advapi32.dll',
|
||||
'c:\\windows\\system32\\crypt32.dll',
|
||||
'c:\\windows\\system32\\cryptdll.dll',
|
||||
'c:\\windows\\system32\\gdi32.dll',
|
||||
'c:\\windows\\system32\\imm32.dll',
|
||||
'c:\\windows\\system32\\msasn1.dll',
|
||||
'c:\\windows\\system32\\msvcrt.dll',
|
||||
'c:\\windows\\system32\\rpcrt4.dll',
|
||||
'c:\\windows\\system32\\rsaenh.dll',
|
||||
'c:\\windows\\system32\\samlib.dll',
|
||||
'c:\\windows\\system32\\sechost.dll',
|
||||
'c:\\windows\\system32\\secur32.dll',
|
||||
'c:\\windows\\system32\\shell32.dll',
|
||||
'c:\\windows\\system32\\shlwapi.dll',
|
||||
'c:\\windows\\system32\\sspicli.dll',
|
||||
'c:\\windows\\system32\\user32.dll',
|
||||
'c:\\windows\\system32\\vaultcli.dll',
|
||||
]
|
||||
|
||||
|
||||
def rule_new_process_create(current_process: process.Process, host, raw_log_data, json_log_data):
|
||||
# 服务端提供了一个 plugin_var 变量用于存放当前进程插件的上下文
|
||||
if current_process.path != 'c:\\windows\\system32\\wbem\\wmic.exe' and current_process.parent_process.path != 'c:\\windows\\system32\\svchost.exe' and current_process.path != 'c:\\windows\\system32\\svchost.exe' and hash_white_list.check_in_while_list(current_process) == False:
|
||||
current_process.plugin_var['mimikatz_matched_num'] = 0
|
||||
current_process.plugin_var['mimikatz_detected'] = False
|
||||
return global_vars.THREAT_TYPE_NONE
|
||||
|
||||
|
||||
def rule_new_process_action(current_process: process.Process, host, raw_log_data, json_log_data):
|
||||
global mimikatz_dll_list
|
||||
# 如果日志的action是imageload(dll加载)
|
||||
if 'mimikatz_detected' in current_process.plugin_var and json_log_data['action'] == 'imageload' and current_process.plugin_var['mimikatz_detected'] == False:
|
||||
# 把日志中的dll路径取出来
|
||||
dll_path = json_log_data['data']['imageloaded']
|
||||
# 如果dll的路径在mimikatz的路径里面,进程上下文+1
|
||||
if dll_path in mimikatz_dll_list:
|
||||
current_process.plugin_var['mimikatz_matched_num'] += 1
|
||||
if current_process.plugin_var['mimikatz_matched_num'] >= len(mimikatz_dll_list):
|
||||
current_process.set_score(300, "[mimikatz]检测到疑似mimikatz进程")
|
||||
current_process.plugin_var['mimikatz_detected'] = True
|
||||
return global_vars.THREAT_TYPE_PROCESS
|
||||
return global_vars.THREAT_TYPE_NONE
|
||||
|
||||
|
||||
def rule_init():
|
||||
pass
|
||||
|
||||
|
||||
def plugin_init():
|
||||
print('mimikatz检测插件 2022/9/5 by huoji')
|
||||
49
Server/plugins/uac_bypass_detect/prcoess_chain_detect.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import global_vars
|
||||
import process
|
||||
#import yara
|
||||
import hash_white_list
|
||||
rm_plugs_config = {
|
||||
"enable": True,
|
||||
"author": "huoji",
|
||||
"description": "基于进程链的uac提权检测",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
||||
|
||||
def intergritylevel_to_int(str_name):
|
||||
if str_name == 'high':
|
||||
return 3
|
||||
elif str_name == 'medium':
|
||||
return 2
|
||||
return 1
|
||||
|
||||
|
||||
def rule_new_process_create(current_process: process.Process, host, raw_log_data, json_log_data):
|
||||
if 'integritylevel' in json_log_data['data']:
|
||||
integritylevel = intergritylevel_to_int(
|
||||
json_log_data['data']['integritylevel'])
|
||||
current_process.plugin_var['uac_flag'] = integritylevel
|
||||
|
||||
if 'uac_flag' not in current_process.chain.root_process.plugin_var:
|
||||
current_process.chain.root_process.plugin_var['uac_flag'] = integritylevel
|
||||
if integritylevel > current_process.chain.root_process.plugin_var['uac_flag'] and hash_white_list.check_in_while_list(current_process) == False:
|
||||
print('[uac bypass detect] detect uac bypass in process chain {}'.format(
|
||||
current_process.path))
|
||||
current_process.chain.root_process.plugin_var['uac_flag'] = integritylevel
|
||||
current_process.set_score(30, "进程权限等级变动")
|
||||
return global_vars.THREAT_TYPE_PROCESS
|
||||
# print('process chain: {} path: {} level: {} log level: {}'.format(
|
||||
# current_process.chain_hash, current_process.path, integritylevel, current_process.chain.root_process.plugin_var['uac_flag']))
|
||||
return global_vars.THREAT_TYPE_NONE
|
||||
|
||||
|
||||
def rule_new_process_action(current_process, host, raw_log_data, json_log_data):
|
||||
return global_vars.THREAT_TYPE_NONE
|
||||
|
||||
|
||||
def rule_init():
|
||||
pass
|
||||
|
||||
|
||||
def plugin_init():
|
||||
print('uac提权插件 2022/8/15 by huoji')
|
||||
@@ -1,8 +1,6 @@
|
||||
|
||||
import json
|
||||
from sqlalchemy import false
|
||||
import tools
|
||||
import time
|
||||
|
||||
skip_process_path = ['c:\\program files\\rivet networks\\smartbyte\\raps.exe',
|
||||
'c:\\program files (x86)\\sogouinput\\11.5.0.5352\\pinyinup.exe',
|
||||
@@ -87,14 +85,17 @@ g_ProcessChainList = []
|
||||
|
||||
|
||||
class Process:
|
||||
def __init__(self, pid, ppid, path, params, time, md5, user, host):
|
||||
def __init__(self, pid, ppid, path, params, time, md5, user, host, is_white=False):
|
||||
self.pid = pid
|
||||
self.parent_process = None
|
||||
self.ppid = ppid
|
||||
self.path = path
|
||||
self.params = params
|
||||
self.chain_hash = ''
|
||||
self.active = True
|
||||
self.operationlist = {}
|
||||
self.attck_hit_list = {}
|
||||
|
||||
self.risk_score = 0
|
||||
self.terminate = False
|
||||
self.rmpid = tools.get_md5(
|
||||
@@ -102,10 +103,12 @@ class Process:
|
||||
self.time = time
|
||||
self.rmppid = ""
|
||||
self.root_rmpid = ""
|
||||
self.plugin_var = {}
|
||||
self.md5 = md5
|
||||
self.user = user
|
||||
self.chain: ProcessChain = None
|
||||
self.host = host
|
||||
self.is_white = is_white
|
||||
|
||||
def set_chain_data(self, chain):
|
||||
self.chain = chain
|
||||
@@ -119,6 +122,15 @@ class Process:
|
||||
def set_rmppid(self, rmppid):
|
||||
self.rmppid = rmppid
|
||||
|
||||
def set_attck(self, new_score, t, name):
|
||||
if t not in self.attck_hit_list:
|
||||
self.risk_score += new_score
|
||||
self.attck_hit_list[t] = name
|
||||
|
||||
if t not in self.chain.attck_hit_list:
|
||||
self.chain.risk_score += new_score
|
||||
self.chain.attck_hit_list[t] = name
|
||||
|
||||
def set_score(self, new_score, opertion):
|
||||
if opertion not in self.operationlist:
|
||||
self.risk_score += new_score
|
||||
@@ -143,6 +155,7 @@ class ProcessChain:
|
||||
self.terminate_count = 0
|
||||
self.risk_score = 0
|
||||
self.operationlist = {}
|
||||
self.attck_hit_list = {}
|
||||
self.process_list = []
|
||||
self.json_arrays = []
|
||||
self.active = True
|
||||
@@ -150,6 +163,7 @@ class ProcessChain:
|
||||
self.rpc_process_chain = ""
|
||||
self.time = root_process.time
|
||||
self.host = root_process.host
|
||||
self.plugin_var = {}
|
||||
self.add_root_process(root_process)
|
||||
|
||||
def get_operationlist(self):
|
||||
@@ -230,6 +244,7 @@ class ProcessChain:
|
||||
"rmppid": proc_info.rmppid,
|
||||
"params": proc_info.params,
|
||||
"operationlist": proc_info.operationlist,
|
||||
"attck_hit_list": proc_info.attck_hit_list,
|
||||
"md5": proc_info.md5,
|
||||
"active": proc_info.active,
|
||||
"children": []
|
||||
|
||||
144
Server/rule.py
@@ -1,82 +1,132 @@
|
||||
import rule_engine
|
||||
import rules.py.process as rule_process
|
||||
import rules.py.action as rule_action
|
||||
import rules.py.attck.process as attck_process
|
||||
import rules.py.attck.attck as attack_software
|
||||
import rules.py.attck.action as attack_action
|
||||
import rules.py.ioa.action as ioa_action
|
||||
import rules.py.ioa.process as ioa_process
|
||||
|
||||
import plugin
|
||||
g_sample_rule = {}
|
||||
g_sample_rule['process'] = rule_process.rule
|
||||
g_sample_rule['action'] = rule_action.rule
|
||||
g_sample_rule['attack_process'] = attck_process.rule
|
||||
g_sample_rule['attack_action'] = attack_action.rule
|
||||
g_sample_rule['attack_software'] = attack_software.rule
|
||||
g_sample_rule['ioa_action'] = ioa_action.rule
|
||||
g_sample_rule['ioa_process'] = ioa_process.rule
|
||||
attck_process_rules = []
|
||||
attck_action_rules = []
|
||||
ioa_process_rules = []
|
||||
ioa_action_rules = []
|
||||
|
||||
base_process_rules = []
|
||||
base_action_rules = []
|
||||
base_host_rules = []
|
||||
|
||||
|
||||
def calc_score_in_action(log):
|
||||
global base_action_rules
|
||||
for iter in base_action_rules:
|
||||
for rule in iter['rules']:
|
||||
# 这是or
|
||||
try:
|
||||
if rule.matches(log):
|
||||
return iter['score'], iter['name']
|
||||
except:
|
||||
print("error: {} ".format(log))
|
||||
def match_att_ck_software(t_list):
|
||||
# 返回是否命中,命中命中,分数
|
||||
|
||||
return 0, ''
|
||||
global g_sample_rule
|
||||
is_match = False
|
||||
match_name = ''
|
||||
match_score = 0
|
||||
for iter in g_sample_rule['attack_software']:
|
||||
rule_list = iter['rules']
|
||||
min_match_num = iter['hit_num']
|
||||
|
||||
match_num = 0
|
||||
|
||||
for t in t_list.keys():
|
||||
if t in rule_list:
|
||||
match_num += 1
|
||||
if match_num >= min_match_num:
|
||||
is_match = True
|
||||
match_name = iter['name']
|
||||
match_score = iter['score']
|
||||
break
|
||||
if is_match:
|
||||
break
|
||||
return is_match, match_name, match_score
|
||||
|
||||
|
||||
def calc_score_in_action(log):
|
||||
# 返回 是否匹配到,是否ioa,attck,分数,名字
|
||||
|
||||
global attck_action_rules
|
||||
global ioa_action_rules
|
||||
for iter in ioa_action_rules:
|
||||
for rule in iter['rules']:
|
||||
if rule.matches(log):
|
||||
return True, True, iter['attck_hit'], iter['score'], iter['name']
|
||||
for iter in attck_action_rules:
|
||||
for rule in iter['rules']:
|
||||
if rule.matches(log):
|
||||
return True, False, iter['attck_hit'], iter['score'], iter['name']
|
||||
return False, False, [], 0, ''
|
||||
|
||||
|
||||
def calc_score_in_create_process(log):
|
||||
global base_process_rules
|
||||
for iter in base_process_rules:
|
||||
# 返回 是否匹配到,是否ioa,attck,分数,名字
|
||||
global ioa_process_rules
|
||||
global attck_process_rules
|
||||
for iter in ioa_process_rules:
|
||||
for rule in iter['rules']:
|
||||
# 这是or
|
||||
if rule.matches(log):
|
||||
return iter['score'], iter['name']
|
||||
return 0, ''
|
||||
|
||||
|
||||
def calc_score_in_host(log):
|
||||
global base_host_rules
|
||||
for iter in base_host_rules:
|
||||
return True, True, iter['attck_hit'], iter['score'], iter['name']
|
||||
for iter in attck_process_rules:
|
||||
for rule in iter['rules']:
|
||||
# 这是or
|
||||
if rule.matches(log):
|
||||
return iter['score'], iter['name']
|
||||
return 0, ''
|
||||
return True, False, iter['attck_hit'], iter['score'], iter['name']
|
||||
return False, False, [], 0, ''
|
||||
|
||||
|
||||
def init_rule():
|
||||
global base_process_rules
|
||||
global base_action_rules
|
||||
global base_host_rules
|
||||
for iter in g_sample_rule['process']:
|
||||
global attck_process_rules
|
||||
global attck_action_rules
|
||||
global ioa_process_rules
|
||||
global ioa_action_rules
|
||||
for iter in g_sample_rule['attack_process']:
|
||||
temp_process_rules = []
|
||||
score = 0
|
||||
if 'score' not in iter:
|
||||
score = 5
|
||||
else:
|
||||
score = iter['score']
|
||||
for iter_i in iter['rules']:
|
||||
print(iter_i)
|
||||
print('rule: {} score: {}'.format(iter_i, score))
|
||||
temp_process_rules.append(rule_engine.Rule(
|
||||
iter_i
|
||||
))
|
||||
base_process_rules.append(
|
||||
{'name': iter['name'], 'score': iter['score'], 'rules': temp_process_rules})
|
||||
for iter in g_sample_rule['action']:
|
||||
attck_process_rules.append(
|
||||
{'name': iter['name'], 'attck_hit': iter['attck_hit'], 'score': score, 'rules': temp_process_rules})
|
||||
for iter in g_sample_rule['attack_action']:
|
||||
temp_process_rules = []
|
||||
score = 0
|
||||
if 'score' not in iter:
|
||||
score = 5
|
||||
else:
|
||||
score = iter['score']
|
||||
for iter_i in iter['rules']:
|
||||
print(iter_i)
|
||||
print('rule: {} score: {}'.format(iter_i, score))
|
||||
temp_process_rules.append(rule_engine.Rule(
|
||||
iter_i
|
||||
))
|
||||
base_action_rules.append(
|
||||
{'name': iter['name'], 'score': iter['score'], 'rules': temp_process_rules})
|
||||
'''
|
||||
for iter in g_sample_rule['host']:
|
||||
attck_action_rules.append(
|
||||
{'name': iter['name'], 'attck_hit': iter['attck_hit'], 'score': score, 'rules': temp_process_rules})
|
||||
for iter in g_sample_rule['ioa_action']:
|
||||
temp_process_rules = []
|
||||
for iter_i in iter['rules']:
|
||||
print(iter_i)
|
||||
print('rule: {} score: {}'.format(iter_i, score))
|
||||
temp_process_rules.append(rule_engine.Rule(
|
||||
iter_i
|
||||
))
|
||||
base_host_rules.append(
|
||||
{'name': iter['name'], 'score': iter['score'], 'rules': temp_process_rules})
|
||||
'''
|
||||
ioa_action_rules.append(
|
||||
{'name': iter['name'], 'attck_hit': iter['attck_hit'], 'score': iter['score'], 'rules': temp_process_rules})
|
||||
for iter in g_sample_rule['ioa_process']:
|
||||
temp_process_rules = []
|
||||
for iter_i in iter['rules']:
|
||||
print('rule: {} score: {}'.format(iter_i, score))
|
||||
temp_process_rules.append(rule_engine.Rule(
|
||||
iter_i
|
||||
))
|
||||
ioa_process_rules.append(
|
||||
{'name': iter['name'], 'attck_hit': iter['attck_hit'], 'score': iter['score'], 'rules': temp_process_rules})
|
||||
plugin.dispath_rule_init()
|
||||
print('init rule done')
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
rule = [
|
||||
{
|
||||
'rules': [
|
||||
'action == "filecreate" and targetfilename =~ "c:\\users\\.*\\appdata\\roaming\\microsoft\\outlook\\vbaproject.otm"'
|
||||
],
|
||||
'score': 300,
|
||||
'name': '已知Outlook模板宏持久化行为'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "processaccess" and targetimage =~ ".*lsass.exe" and grantedaccess & 0x0010 and sourceimage =~ ".*rundll32.exe"',
|
||||
@@ -17,7 +24,7 @@ rule = [
|
||||
'rules': [
|
||||
'action == "processaccess" and calltrace =~ ".*unknown.*" and not calltrace =~ ".*conpty\.node.*" and not calltrace =~ ".*java\.dll.*" and not calltrace =~ ".*appvisvsubsystems64\.dll.*" and not calltrace =~ ".*twinui\.dll.*" and not calltrace =~ ".*nativeimages.*" and not targetimage == "c:\\windows\\system32\\cmd.exe"',
|
||||
],
|
||||
'score': 40,
|
||||
'score': 20,
|
||||
'name': '异常进程访问'
|
||||
},
|
||||
{
|
||||
@@ -161,5 +168,12 @@ rule = [
|
||||
],
|
||||
'score': 50,
|
||||
'name': '创建可疑文件'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "imageload" and imageloaded == "c:\\windows\\system32\\samlib.dll"',
|
||||
],
|
||||
'score': 10,
|
||||
'name': 'samlib的dll被加载'
|
||||
}
|
||||
]
|
||||
|
||||
165
Server/rules/py/attck/action.py
Normal file
@@ -0,0 +1,165 @@
|
||||
rule = [
|
||||
{
|
||||
'rules': [
|
||||
'action == "registryvalueset" and targetobject =~ ".*proxyenable"',
|
||||
],
|
||||
'attck_hit':['T1562.001'],
|
||||
'name': 'Impair Defenses: Disable or Modify Tools'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "processaccess" and calltrace =~ ".*unknown.*" and not calltrace =~ ".*conpty\.node.*" and not calltrace =~ ".*java\.dll.*" and not calltrace =~ ".*appvisvsubsystems64\.dll.*" and not calltrace =~ ".*twinui\.dll.*" and not calltrace =~ ".*nativeimages.*" and not targetimage == "c:\\windows\\system32\\cmd.exe"',
|
||||
],
|
||||
'attck_hit':['T1620'],
|
||||
'name': 'Reflective Code Loading'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "processaccess" and calltrace =~ ".*wshom\.ocx.*"',
|
||||
'action == "processaccess" and calltrace =~ ".*shell32\.dll.*"',
|
||||
'action == "processaccess" and calltrace =~ ".*dbgcore\.dll.*"',
|
||||
'action == "processaccess" and calltrace =~ ".*kernelbase\.dll\+de67e.*"',
|
||||
'action == "processaccess" and calltrace =~ ".*framedynos\.dll.*"',
|
||||
],
|
||||
'attck_hit':['T1559.001'],
|
||||
'name': 'Inter-Process Communication: Component Object Model'
|
||||
},
|
||||
# todo 懒得做详细的规则了.加油完善规则吧
|
||||
{
|
||||
'rules': [
|
||||
'action == "createremotethread"',
|
||||
],
|
||||
'attck_hit':['T1055'],
|
||||
'score': 30,
|
||||
'name': 'Process Injection'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "filecreatestreamhash"',
|
||||
],
|
||||
'attck_hit':['T1564.004'],
|
||||
'name': 'Hide Artifacts: NTFS File Attributes'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "dnsquery"',
|
||||
],
|
||||
'attck_hit':['T1071.004'],
|
||||
'name': 'Application Layer Protocol: DNS'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "filecreatetimechange"',
|
||||
],
|
||||
'attck_hit':['T1070.006'],
|
||||
'name': 'Indicator Removal on Host: Timestomp'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "networkconnect"',
|
||||
],
|
||||
'attck_hit':['T1071'],
|
||||
'name': 'Application Layer Protocol'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "clipboardchange"',
|
||||
],
|
||||
'attck_hit':['T1115'],
|
||||
'name': 'Clipboard Data Monitor API'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "processtampering"',
|
||||
],
|
||||
'attck_hit':['T1574'],
|
||||
'name': 'Hijack Execution Flow'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "filecreate" and targetfilename =~ "c:\\\\\\\\windows\\\\\\\\.*"',
|
||||
'action == "filecreate" and targetfilename =~ ".*\.exe"',
|
||||
'action == "filecreate" and targetfilename =~ ".*\.cmd"',
|
||||
'action == "filecreate" and targetfilename =~ ".*\.bat"',
|
||||
'action == "filecreate" and targetfilename =~ ".*\.dll"',
|
||||
],
|
||||
'attck_hit':['T1036.005'],
|
||||
'name': 'Masquerading: Match Legitimate Name or Location'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "filecreate" and targetfilename =~ "c:\\\\\\\\windows\\\\\\\\.*"',
|
||||
],
|
||||
'attck_hit':['T1036.005'],
|
||||
'name': 'Masquerading: Match Legitimate Name or Location'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "filecreate" and targetfilename =~ "c:\\\\\\\\users\\\\\\\\.*"',
|
||||
'action == "filecreate" and targetfilename =~ ".*\.exe"',
|
||||
'action == "filecreate" and targetfilename =~ ".*\.cmd"',
|
||||
'action == "filecreate" and targetfilename =~ ".*\.bat"',
|
||||
'action == "filecreate" and targetfilename =~ ".*\.dll"',
|
||||
],
|
||||
'attck_hit':['T1036.005'],
|
||||
'name': 'Masquerading: Match Legitimate Name or Location'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "imageload" and imageloaded == "c:\\windows\\system32\\samlib.dll"',
|
||||
],
|
||||
'attck_hit':['T1003.002'],
|
||||
'name': 'OS Credential Dumping: Security Account Manager'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "imageload" and imageloaded =~ ".*credui.dll"',
|
||||
],
|
||||
'attck_hit':['T1047'],
|
||||
'name': 'Windows Management Instrumentation'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "imageload" and imageloaded =~ ".*dbghelp.dll"',
|
||||
],
|
||||
'attck_hit':['T1622'],
|
||||
'name': 'Debugger Evasion'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "imageload" and imageloaded =~ ".*winhttp.dll"',
|
||||
'action == "imageload" and imageloaded =~ ".*urlmon.dll"',
|
||||
],
|
||||
'attck_hit':['T1071.001'],
|
||||
'name': 'Application Layer Protocol: Web Protocols'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "imageload" and imageloaded =~ ".*dnsapi.dll"',
|
||||
],
|
||||
'attck_hit':['T1071.004'],
|
||||
'name': 'Application Layer Protocol: DNS'
|
||||
},
|
||||
# 不应该用dll来当T的,这里应该是api的hook.但是sysmon没这些ds,只能凑合.这非常不专业
|
||||
{
|
||||
'rules': [
|
||||
'action == "imageload" and imageloaded =~ ".*rtutils.dll"',
|
||||
],
|
||||
'attck_hit':['CMT0001'],
|
||||
'name': 'Event trace manipulation'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "imageload" and imageloaded =~ ".*rasapi32.dll"',
|
||||
],
|
||||
'attck_hit':['CMT0002'],
|
||||
'name': 'rasapi32 manipulation'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "imageload" and imageloaded =~ ".*napinsp.dll"',
|
||||
],
|
||||
'attck_hit':['CMT0003'],
|
||||
'name': 'napinsp manipulation'
|
||||
}
|
||||
]
|
||||
12
Server/rules/py/attck/attck.py
Normal file
@@ -0,0 +1,12 @@
|
||||
rule = [
|
||||
{'name': "BRC4", 'rules': ['T1071', 'T1071.001',
|
||||
'T1622', 'T1047', 'T1562.001'], 'hit_num': 4, 'score':100},
|
||||
{'name': "BRC4#2", 'rules': ['T1071.004',
|
||||
'T1071.001', 'T1562.001', 'CMT0001', 'CMT0002', 'CMT0003'], 'hit_num': 6, 'score':100},
|
||||
{'name': "Ransomware", 'rules': ['T1071',
|
||||
'T1036.005', 'T1620', 'T1564.001', 'T1222.001', 'T1059.005', 'T1543.003', 'T1490'], 'hit_num': 7, 'score':100},
|
||||
{'name': "APT-System discovery", 'rules': ['T1018',
|
||||
'T1087.001', 'T1087.001', 'T1082', 'T1016'], 'hit_num': 3, 'score':65},
|
||||
{'name': "APT-Hydra", 'rules': ['T1027.004',
|
||||
'T1018', 'T1559.001', 'T1218.011', 'T1059.001', 'T1059.005', 'T1570', 'T1087.002', 'T1564', 'T1106', 'T1082', 'T1087.001', 'T1003', 'T1071'], 'hit_num': 10, 'score':100}
|
||||
]
|
||||
314
Server/rules/py/attck/process.py
Normal file
@@ -0,0 +1,314 @@
|
||||
rule = [
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename == "taskill.exe"',
|
||||
'originalfilename == "net.exe" and commandline =~ ".*stop.*"',
|
||||
'originalfilename == "sc.exe" and commandline =~ ".*config.*" and commandline =~ ".*disabled.*"',
|
||||
],
|
||||
'attck_hit':['T1489'],
|
||||
'score': 30,
|
||||
'name': 'Service Stop'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*curl.exe" or originalfilename =~ ".*wget.exe" or originalfilename =~ ".*dget.exe"',
|
||||
'originalfilename =~ ".*certutil.exe"',
|
||||
'originalfilename =~ ".*powershell.exe" and commandline =~ ".*invoke-webrequest.*"'
|
||||
],
|
||||
'attck_hit':['T1105'],
|
||||
'score': 30,
|
||||
'name':'Ingress Tool Transfer'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'image =~ ".*\.doc\.exe"',
|
||||
'image =~ ".*\.docx\.exe"',
|
||||
'image =~ ".*\.ppt\.exe"',
|
||||
'image =~ ".*\.pdf\.exe"',
|
||||
'image =~ ".*\.html\.exe"',
|
||||
'image =~ ".*\.htm\.exe"',
|
||||
'image =~ ".*\.zip\.exe"',
|
||||
'image =~ ".*\.rar\.exe"'
|
||||
],
|
||||
'attck_hit':['T1036.007'],
|
||||
'score': 60,
|
||||
'name':'Masquerading: Double File Extension'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'commandline =~ ".*-k dcomlaunch.*"'
|
||||
],
|
||||
'attck_hit':['T1559.001'],
|
||||
'score': 30,
|
||||
'name':'Inter-Process Communication: Component Object Model'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*vssadmin.exe" and commandline =~ ".*create.*"',
|
||||
],
|
||||
'attck_hit':['T1003.003'],
|
||||
'score': 30,
|
||||
'name':'OS Credential Dumping: NTDS'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*wbadmin.exe" and commandline =~ ".*delete.*"',
|
||||
'originalfilename =~ ".*bcdedit.exe" and commandline =~ ".*recoveryenabled.*no.*"',
|
||||
'originalfilename =~ ".*bcdedit.exe" and commandline =~ ".*bootstatuspolicy.*ignoreallfailures.*"',
|
||||
'originalfilename =~ ".*wmic.exe" and commandline =~ ".*shadowcopy.*" and commandline =~ ".*delete.*"',
|
||||
'originalfilename =~ ".*vssadmin.exe" and commandline =~ ".*shadows.*" and commandline =~ ".*delete.*"',
|
||||
],
|
||||
'attck_hit':['T1490'],
|
||||
'score': 30,
|
||||
'name': 'Inhibit System Recovery'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename == "net.exe" and commandline =~ ".*view.*"',
|
||||
'originalfilename == "net.exe" and commandline =~ ".*group.*"',
|
||||
'originalfilename == "ping.exe"',
|
||||
|
||||
],
|
||||
'attck_hit':['T1018'],
|
||||
'score': 10,
|
||||
'name': 'Remote System Discovery'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*fsutil.exe" and commandline =~ ".*deletejournal.*"',
|
||||
],
|
||||
'attck_hit':['T1070.004'],
|
||||
'score': 10,
|
||||
'name': 'Indicator Removal on Host'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename == ".*net.exe" and commandline =~ ".*user.*"',
|
||||
'originalfilename =~ ".*whoami.exe"',
|
||||
'originalfilename =~ ".*query.exe"',
|
||||
'originalfilename =~ ".*setspn.exe"',
|
||||
'originalfilename =~ ".*cmdkey.exe"'
|
||||
],
|
||||
'attck_hit':['T1087.001'],
|
||||
'score': 30,
|
||||
'name': 'Account Discovery: Local Account'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*wmic.exe" and commandline =~ ".*useraccount.*"',
|
||||
],
|
||||
'attck_hit':['T1087.001', 'T1047'],
|
||||
'score': 30,
|
||||
'name': 'Account Discovery: Local Account by wmic'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*wmic.exe" and commandline =~ ".*startup.*"',
|
||||
'originalfilename =~ ".*wmic.exe" and commandline =~ ".*share.*"',
|
||||
|
||||
],
|
||||
'attck_hit':['T1082', 'T1047'],
|
||||
'score': 30,
|
||||
'name': 'System Information Discovery by wmic'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*systeminfo.exe"',
|
||||
'originalfilename =~ ".*chcp.com"'
|
||||
|
||||
],
|
||||
'attck_hit':['T1082'],
|
||||
'score': 10,
|
||||
'name': 'System Information Discovery'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*tasklist.exe"',
|
||||
],
|
||||
'attck_hit':['T1057'],
|
||||
'score': 10,
|
||||
'name': 'Process Discovery'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename == "at.exe"',
|
||||
],
|
||||
'attck_hit':['T1053.002'],
|
||||
'score': 10,
|
||||
'name': 'Scheduled Task/Job: at'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*schtasks.exe.*"',
|
||||
],
|
||||
'attck_hit':['T1053.005'],
|
||||
'score': 10,
|
||||
'name': 'Scheduled Task/Job: Scheduled Task'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'image =~ ".*\\\\\\\\appdata\\\\\\\\local\\\\\\\\temp\\\\\\\\.*" or image =~ ".*\\\\\\\\windows\\\\\\\\temp\\\\\\\\.*"',
|
||||
],
|
||||
'attck_hit':['T1106'],
|
||||
'score': 10,
|
||||
'name': 'Execution: Native API'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*rubeus.*" and commandline =~ ".*domain.*"',
|
||||
],
|
||||
'attck_hit':['T1558.003'],
|
||||
'score': 10,
|
||||
'name': 'Steal or Forge Kerberos Tickets: Kerberoasting'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*\u202e.*"',
|
||||
],
|
||||
'attck_hit':['T1564'],
|
||||
'score': 10,
|
||||
'name': 'Hide Artifacts'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'parentimage =~ ".*mmc.exe" and commandline =~ ".*eventvwr\.msc.*"',
|
||||
],
|
||||
'attck_hit':['T1218.014'],
|
||||
'score': 10,
|
||||
'name': 'System Binary Proxy Execution: MMC'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename == "net.exe" and commandline =~ ".*domain.*"',
|
||||
'originalfilename == "net.exe" and commandline =~ ".*view.*"',
|
||||
'originalfilename == "net.exe" and commandline =~ ".*workstation.*"'
|
||||
],
|
||||
'attck_hit':['T1087.002'],
|
||||
'score': 10,
|
||||
'name': 'Account Discovery: Domain Account'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename == "netsh.exe" and commandline =~ ".*firewall.*"',
|
||||
],
|
||||
'attck_hit':['T1562.004'],
|
||||
'score': 10,
|
||||
'name': 'Impair Defenses: Disable or Modify System Firewall'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*ipconfig.exe"',
|
||||
'originalfilename =~ ".*netstat.exe"'
|
||||
|
||||
],
|
||||
'attck_hit':['T1016'],
|
||||
'score': 10,
|
||||
'name': 'System Network Configuration Discovery'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*attrib.exe"',
|
||||
],
|
||||
'attck_hit':['T1564.001'],
|
||||
'score': 10,
|
||||
'name': 'Hide Artifacts: Hidden Files and Directories'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*psexesvc.exe"',
|
||||
],
|
||||
'attck_hit':['T1570'],
|
||||
'score': 10,
|
||||
'name': 'Lateral Tool Transfer'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ "\\\\\\\\.*\\\\\\C\$.*"',
|
||||
],
|
||||
'attck_hit':['T1080'],
|
||||
'score': 10,
|
||||
'name': 'Taint Shared Content'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*icacls.exe"',
|
||||
],
|
||||
'attck_hit':['T1222.001'],
|
||||
'score': 10,
|
||||
'name': 'Windows File and Directory Permissions Modification'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'parentimage =~ ".*services.exe"',
|
||||
],
|
||||
'attck_hit':['T1543.003'],
|
||||
'score': 10,
|
||||
'name': 'Create or Modify System Process: Windows Service'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*werfault.exe" and parentimage =~ ".*svchost.exe"',
|
||||
],
|
||||
'attck_hit':['T1218'],
|
||||
'score': 10,
|
||||
'name': 'System Binary Proxy Execution'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*wscript.exe"',
|
||||
'originalfilename =~ ".*cscript.exe"',
|
||||
],
|
||||
'attck_hit':['T1059.005'],
|
||||
'score': 10,
|
||||
'name': 'Command and Scripting Interpreter: Visual Basic'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*mofcomp.exe.*"'
|
||||
],
|
||||
'attck_hit':['T1546.015'],
|
||||
'score': 10,
|
||||
'name':'Event Triggered Execution: Component Object Model Hijacking'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*csc.exe.*"'
|
||||
],
|
||||
'attck_hit':['T1027.004'],
|
||||
'score': 10,
|
||||
'name':'Compile After Delivery'
|
||||
},
|
||||
# https://attack.mitre.org/software/S0552/
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*adfind.exe.*"'
|
||||
],
|
||||
'attck_hit':['T1018'],
|
||||
'score': 10,
|
||||
'name':'Remote System Discovery'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename == "wmic.exe"'
|
||||
],
|
||||
'attck_hit':['T1559.001'],
|
||||
'score': 30,
|
||||
'name':'Windows Management Instrumentation'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*rundll32.exe.*"'
|
||||
],
|
||||
'attck_hit':['T1218.011'],
|
||||
'score': 10,
|
||||
'name':'System Binary Proxy Execution: Rundll32'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*powershell.exe"'
|
||||
],
|
||||
'attck_hit':['T1059.001'],
|
||||
'score': 10,
|
||||
'name':'Command and Scripting Interpreter: PowerShell'
|
||||
},
|
||||
]
|
||||
58
Server/rules/py/ioa/action.py
Normal file
@@ -0,0 +1,58 @@
|
||||
rule = [
|
||||
{
|
||||
'rules': [
|
||||
'action == "processaccess" and targetimage =~ ".*lsass.exe"',
|
||||
],
|
||||
'attck_hit':['T1003'],
|
||||
'score': 100,
|
||||
'name': 'OS Credential Dumping: LSASS Memory'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "processaccess" and targetimage =~ ".*lsass.exe" and grantedaccess & 0x0010 and sourceimage =~ ".*rundll32.exe"',
|
||||
],
|
||||
'attck_hit':['T1003.002'],
|
||||
'score': 100,
|
||||
'name': '已知内存加载mimikazt行为'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "processaccess" and sourceimage =~ ".*office16.*" and calltrace =~ ".*kernelbase\.dll.*"',
|
||||
],
|
||||
'attck_hit':['T1003.002'],
|
||||
'score': 60,
|
||||
'name': 'office异常进程内存'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "pipecreate" and pipename =~ ".*msagent.*"',
|
||||
'action == "pipecreate" and pipename =~ ".*msse.*"',
|
||||
'action == "pipecreate" and pipename =~ ".*postex_.*"',
|
||||
'action == "pipecreate" and pipename =~ ".*postex_ssh.*"',
|
||||
'action == "pipecreate" and pipename =~ ".*status_.*"',
|
||||
],
|
||||
'attck_hit':['T1003.002'],
|
||||
'score': 100,
|
||||
'name': '已知CobalStrike'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "pipecreate" and pipename =~ ".*paexec.*"',
|
||||
'action == "pipecreate" and pipename =~ ".*remcom.*"',
|
||||
'action == "pipecreate" and pipename =~ ".*csexec.*"'
|
||||
],
|
||||
'attck_hit':['T1003.002'],
|
||||
'score': 100,
|
||||
'name': '已知内网横向工具'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'action == "pipecreate" and pipename =~ ".*lsadump.*"',
|
||||
'action == "pipecreate" and pipename =~ ".*cachedump.*"',
|
||||
'action == "pipecreate" and pipename =~ ".*wceservicepipe.*"'
|
||||
],
|
||||
'attck_hit':['T1003.002'],
|
||||
'score': 100,
|
||||
'name': '已知mimikazt内存dump'
|
||||
},
|
||||
]
|
||||
35
Server/rules/py/ioa/process.py
Normal file
@@ -0,0 +1,35 @@
|
||||
rule = [
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*todesk_service.*" or originalfilename =~ ".*sunloginclient.*" or originalfilename =~ ".*teamviewer_service.exe.*" or originalfilename =~ ".*logmein.*" or originalfilename =~ ".*dwrcs.*" or originalfilename =~ ".*aa_v3.*" or originalfilename =~ ".*screenconnect.*" or originalfilename =~ ".*tvnserver.*" or originalfilename =~ ".*vncserver.*"',
|
||||
],
|
||||
'attck_hit':['T1133'],
|
||||
'score': 30,
|
||||
'name': '已知远程协助程序'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*phoenixminer.*" or originalfilename =~ ".*ccminer.*" or originalfilename =~ ".*csminer.exe.*" or originalfilename =~ ".*xmrig.*" or originalfilename =~ ".*xmr-stak.*"',
|
||||
],
|
||||
'attck_hit':['T1496'],
|
||||
'score': 100,
|
||||
'name': '已知挖矿程序'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ "\\\\\\.*" and parentimage =~ ".*services.exe"',
|
||||
],
|
||||
'attck_hit':['T1021.006'],
|
||||
'score': 100,
|
||||
'name': '远程服务被创建'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'commandline =~ ".*__\d{10}\."',
|
||||
'originalfilename =~ ".*wmi_share.exe"',
|
||||
],
|
||||
'attck_hit':['T00000'],
|
||||
'score': 100,
|
||||
'name': 'wmic内网横向移动被触发'
|
||||
},
|
||||
]
|
||||
@@ -1,390 +0,0 @@
|
||||
rule = [
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*taskill.exe.*"',
|
||||
'originalfilename =~ ".*net.exe.*" and commandline =~ ".*stop.*"',
|
||||
'originalfilename =~ ".*sc.exe.*" and commandline =~ ".*config.*" and commandline =~ ".*disabled.*"',
|
||||
],
|
||||
'score': 40,
|
||||
'name': '通过系统程序关闭进程'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*curl.exe" or originalfilename =~ ".*wget.exe" or originalfilename =~ ".*dget.exe"'
|
||||
],
|
||||
'score': 40,
|
||||
'name':'通过应用下载文件'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'image =~ ".*\.doc\.exe"',
|
||||
'image =~ ".*\.docx\.exe"',
|
||||
'image =~ ".*\.ppt\.exe"',
|
||||
'image =~ ".*\.pdf\.exe"',
|
||||
'image =~ ".*\.html\.exe"',
|
||||
'image =~ ".*\.htm\.exe"',
|
||||
'image =~ ".*\.zip\.exe"',
|
||||
'image =~ ".*\.rar\.exe"'
|
||||
],
|
||||
'score': 30,
|
||||
'name':'启动双扩展名文件'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'commandline =~ ".*-k dcomlaunch.*"'
|
||||
],
|
||||
'score': 30,
|
||||
'name':'通过DCOM启动了进程'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*wbadmin.exe.*" and commandline =~ ".*delete.*"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过wbadmin删除备份'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*net.exe.*" and commandline =~ ".*view.*"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过net进行远程系统发现'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*fsutil.exe.*" and commandline =~ ".*deletejournal.*"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过系统工具删除USN'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*net.exe.*" and commandline =~ ".*user.*"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过net进行系统用户发现'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*schtasks.exe.*" and commandline =~ ".*create.*"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过系统应用创建计划任务'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*schtasks.exe.*" and commandline =~ ".*delete.*"',
|
||||
],
|
||||
'score': 40,
|
||||
'name': '通过系统应用删除计划任务'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*vssadmin.exe.*" and commandline =~ ".*create.*"',
|
||||
],
|
||||
'score': 40,
|
||||
'name': '通过系统程序创建卷影备份'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*todesk_service.*" or originalfilename =~ ".*sunloginclient.*" or originalfilename =~ ".*teamviewer_service.exe.*" or originalfilename =~ ".*logmein.*" or originalfilename =~ ".*dwrcs.*" or originalfilename =~ ".*aa_v3.*" or originalfilename =~ ".*screenconnect.*" or originalfilename =~ ".*tvnserver.*" or originalfilename =~ ".*vncserver.*"',
|
||||
],
|
||||
'score': 20,
|
||||
'name': '已知远程协助程序'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*phoenixminer.*" or originalfilename =~ ".*ccminer.*" or originalfilename =~ ".*csminer.exe.*" or originalfilename =~ ".*xmrig.*" or originalfilename =~ ".*xmr-stak.*"',
|
||||
],
|
||||
'score': 300,
|
||||
'name': '已知挖矿程序'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'image =~ ".*\\\\\\\\appdata\\\\\\\\local\\\\\\\\temp\\\\\\\\.*" or image =~ ".*\\\\\\\\windows\\\\\\\\temp\\\\\\\\.*"',
|
||||
],
|
||||
'score': 40,
|
||||
'name': '从临时文件创建进程'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*rubeus.*" and commandline =~ ".*domain.*"',
|
||||
],
|
||||
'score': 100,
|
||||
'name': '通过系统工具获取域登陆令牌'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*whoami.*"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': 'whoami被执行'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*\u202e.*"',
|
||||
],
|
||||
'score': 100,
|
||||
'name': '伪装名字程序被执行'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'parentimage =~ ".*mmc.exe" and commandline =~ ".*eventvwr\.msc.*"',
|
||||
],
|
||||
'score': 40,
|
||||
'name': '高权限进程被创建'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*bcdedit.exe" and commandline =~ ".*recoveryenabled.*no.*"',
|
||||
'originalfilename =~ ".*bcdedit.exe" and commandline =~ ".*bootstatuspolicy.*ignoreallfailures.*"',
|
||||
],
|
||||
'score': 80,
|
||||
'name': '通过系统工具关闭系统恢复'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*wmic.exe" and commandline =~ ".*useraccount.*"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过wmic进行系统用户发现'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*wmic.exe" and commandline =~ ".*startup.*"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过wmic查看系统启动项'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*wmic.exe" and commandline =~ ".*share.*"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过wmic查看系统共享'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*wmic.exe" and commandline =~ ".*shadowcopy.*" and commandline =~ ".*delete.*"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': 'wmic删除卷影备份'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*vssadmin.exe" and commandline =~ ".*shadows.*" and commandline =~ ".*delete.*"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': 'vssadmin删除卷影备份'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*tasklist.exe"',
|
||||
],
|
||||
'score': 50,
|
||||
'name': '通过tasklist查看系统信息'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*systeminfo.exe"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过systeminfo查看系统信息'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*query.exe"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过query进行系统用户发现'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*net.exe" and commandline =~ ".*domain.*"',
|
||||
'originalfilename =~ ".*net.exe" and commandline =~ ".*view.*"',
|
||||
'originalfilename =~ ".*net.exe" and commandline =~ ".*workstation.*"'
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过net进行本地系统用户发现'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*setspn.exe"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过setspn进行本地系统用户发现'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*netsh.exe" and commandline =~ ".*firewall.*"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过netsh关闭防火墙'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*cmd.exe" and commandline =~ ".*ipconfig.*"',
|
||||
],
|
||||
'score': 80,
|
||||
'name': 'cmd启动ipconfig'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*cmd.exe" and commandline =~ ".*net.*"',
|
||||
],
|
||||
'score': 60,
|
||||
'name': 'cmd启动net'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*netstat.exe"',
|
||||
],
|
||||
'score': 40,
|
||||
'name': 'netstat被运行'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*ping.exe"',
|
||||
],
|
||||
'score': 40,
|
||||
'name': 'ping被运行'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*ipconfig.exe"',
|
||||
],
|
||||
'score': 40,
|
||||
'name': 'ipconfig被运行'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*attrib.exe"',
|
||||
],
|
||||
'score': 40,
|
||||
'name': 'attrib被运行'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*PSEXESVC.exe"',
|
||||
],
|
||||
'score': 100,
|
||||
'name': 'PSEXESVC内网横向移动'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ "\\\\\\\\.*\\\\\\C\$.*"',
|
||||
],
|
||||
'score': 100,
|
||||
'name': 'SMB共享启动进程'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'commandline =~ ".*__\d{10}\."',
|
||||
'originalfilename =~ ".*wmi_share.exe"',
|
||||
],
|
||||
'score': 100,
|
||||
'name': 'wmic内网横向移动被触发'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*icacls.exe"',
|
||||
],
|
||||
'score': 40,
|
||||
'name': 'icacls被运行'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ "\\\\\\.*" and parentimage =~ ".*services.exe"',
|
||||
],
|
||||
'score': 100,
|
||||
'name': '远程服务被创建'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'parentimage =~ ".*services.exe"',
|
||||
],
|
||||
'score': 30,
|
||||
'name': '从服务创建的进程'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*wscript.exe"',
|
||||
'originalfilename =~ ".*cscript.exe"',
|
||||
],
|
||||
'score': 40,
|
||||
'name': '脚本程序被运行'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*mofcomp.exe.*"'
|
||||
],
|
||||
'score': 80,
|
||||
'name':'注册WMI订阅'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*csc.exe.*"'
|
||||
],
|
||||
'score': 80,
|
||||
'name':'.NET编译器被启动'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*cmdkey.exe.*"'
|
||||
],
|
||||
'score': 100,
|
||||
'name':'通过系统应用查询本机账户'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*adfind.exe.*"'
|
||||
],
|
||||
'score': 80,
|
||||
'name':'通过系统程序发现域信息'
|
||||
},
|
||||
# 这些是保底规则 必须放到最底下才匹配
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*cmd.exe"'
|
||||
],
|
||||
'score': 30,
|
||||
'name':'执行CMD命令'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*chcp.com"'
|
||||
],
|
||||
'score': 30,
|
||||
'name':'执行chcp.com'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*wmic.exe.*"'
|
||||
],
|
||||
'score': 80,
|
||||
'name':'执行wmic'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*rundll32.exe.*"'
|
||||
],
|
||||
'score': 20,
|
||||
'name':'通过rundll32启动进程'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*certutil.exe"',
|
||||
'originalfilename =~ ".*curl.exe"',
|
||||
'originalfilename =~ ".*powershell.exe" and commandline =~ ".*invoke-webrequest.*"'
|
||||
],
|
||||
'score': 80,
|
||||
'name':'通过系统命令下载文件'
|
||||
},
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*powershell.exe"'
|
||||
],
|
||||
'score': 80,
|
||||
'name':'Powershell被执行'
|
||||
},
|
||||
]
|
||||
134
Server/sql.py
@@ -10,6 +10,7 @@ from sqlalchemy import Column, Integer, String, Table
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy import delete
|
||||
import sqlalchemy
|
||||
import json
|
||||
|
||||
g_engine = None
|
||||
@@ -19,6 +20,8 @@ g_rawdata_table = None
|
||||
g_rawdata_table_ins = None
|
||||
g_threat_table = None
|
||||
g_threat_table_ins = None
|
||||
g_hash_white_list_table = None
|
||||
g_hash_white_list_table_ins = None
|
||||
|
||||
|
||||
class raw_process_log(g_base):
|
||||
@@ -61,6 +64,15 @@ class raw_process_log(g_base):
|
||||
return self.id
|
||||
|
||||
|
||||
class hash_white_list(g_base):
|
||||
__tablename__ = "hash_white_list"
|
||||
id = Column(Integer, primary_key=True)
|
||||
hash = Column(String)
|
||||
path = Column(String)
|
||||
timestamp = Column(Integer)
|
||||
reason = Column(String)
|
||||
|
||||
|
||||
class threat_log(g_base):
|
||||
__tablename__ = "threat_log"
|
||||
# 定义各字段
|
||||
@@ -75,6 +87,8 @@ class threat_log(g_base):
|
||||
risk_score = Column(Integer)
|
||||
# 命中的规则
|
||||
hit_rule = Column(String)
|
||||
# attck命中
|
||||
attck_hit_list = Column(String)
|
||||
# json字段
|
||||
data = Column(String)
|
||||
# 时间戳
|
||||
@@ -98,8 +112,11 @@ def init():
|
||||
global g_rawdata_table_ins
|
||||
global g_threat_table
|
||||
global g_threat_table_ins
|
||||
global g_hash_white_list_table
|
||||
global g_hash_white_list_table_ins
|
||||
|
||||
g_engine = create_engine("sqlite:///syseye.db?check_same_thread=False", echo=False)
|
||||
g_engine = create_engine(
|
||||
"sqlite:///syseye.db?check_same_thread=False", echo=False)
|
||||
g_base.metadata.create_all(g_engine)
|
||||
g_metadata = MetaData(g_engine)
|
||||
g_rawdata_table = Table("raw_process_log", g_metadata, autoload=True)
|
||||
@@ -108,6 +125,56 @@ def init():
|
||||
g_threat_table = Table("threat_log", g_metadata, autoload=True)
|
||||
g_threat_table_ins = g_threat_table.insert()
|
||||
|
||||
g_hash_white_list_table = Table(
|
||||
"hash_white_list", g_metadata, autoload=True)
|
||||
g_hash_white_list_table_ins = g_hash_white_list_table.insert()
|
||||
|
||||
|
||||
def query_white_list_by_hash(pHash):
|
||||
global g_hash_white_list_table
|
||||
sql_session = sessionmaker(bind=g_engine)
|
||||
white_list = sql_session().query(
|
||||
g_hash_white_list_table).filter_by(hash=pHash).first()
|
||||
sql_session().close()
|
||||
return white_list
|
||||
|
||||
|
||||
def delete_white_list(pHash):
|
||||
global g_hash_white_list_table
|
||||
global g_engine
|
||||
conn = g_engine.connect()
|
||||
result = conn.execute(
|
||||
delete(g_hash_white_list_table).where(
|
||||
g_hash_white_list_table.columns.hash == pHash)
|
||||
)
|
||||
conn.close()
|
||||
return result
|
||||
|
||||
|
||||
def push_white_list(pPath, pHash, pReason):
|
||||
global g_hash_white_list_table_ins
|
||||
current_time = int(round(time.time() * 1000))
|
||||
ins = g_hash_white_list_table_ins.values(
|
||||
path=pPath, hash=pHash, reason=pReason, timestamp=current_time)
|
||||
# 连接引擎
|
||||
conn = g_engine.connect()
|
||||
# 执行语句
|
||||
result = conn.execute(ins)
|
||||
conn.close()
|
||||
return result
|
||||
|
||||
|
||||
def query_all_white_list():
|
||||
global g_hash_white_list_table
|
||||
sql_session = sessionmaker(bind=g_engine)
|
||||
white_list = (
|
||||
sql_session()
|
||||
.query(g_hash_white_list_table)
|
||||
.all()
|
||||
)
|
||||
sql_session().close()
|
||||
return white_list
|
||||
|
||||
|
||||
def push_process_raw(
|
||||
host,
|
||||
@@ -163,22 +230,43 @@ def push_process_raw(
|
||||
conn = g_engine.connect()
|
||||
# 执行语句
|
||||
result = conn.execute(ins)
|
||||
conn.close()
|
||||
return result
|
||||
|
||||
|
||||
def select_create_process_raw_log_by_time(start, end):
|
||||
def query_last_raw_process_log(num):
|
||||
global g_rawdata_table
|
||||
sql_session = sessionmaker(bind=g_engine)
|
||||
# 用g_rawdata_table 不行, utf8编码问题?
|
||||
start_time = int(round(time.time() * 1000))
|
||||
end_time = start_time - 1000 * 60 * 60 * 24 * 7
|
||||
raw_log = (
|
||||
sql_session()
|
||||
.query(g_rawdata_table)
|
||||
.filter(
|
||||
raw_process_log.timestamp >= start,
|
||||
raw_process_log.timestamp < end,
|
||||
raw_process_log.action == "processcreate",
|
||||
)
|
||||
.query(raw_process_log)
|
||||
# .filter(
|
||||
# raw_process_log.timestamp >= end_time
|
||||
# )
|
||||
.limit(num)
|
||||
.all()
|
||||
)
|
||||
sql_session().close()
|
||||
return raw_log
|
||||
|
||||
|
||||
def select_process_raw_log_by_time(start: int, end: int):
|
||||
global g_rawdata_table
|
||||
sql_session = sessionmaker(bind=g_engine)
|
||||
# 用g_rawdata_table 不行, utf8编码问题?
|
||||
raw_log = (
|
||||
sql_session()
|
||||
.query(raw_process_log)
|
||||
.filter(
|
||||
sqlalchemy.and_(
|
||||
raw_process_log.timestamp >= start, raw_process_log.timestamp < end
|
||||
)
|
||||
)
|
||||
.all()
|
||||
)
|
||||
sql_session().close()
|
||||
return raw_log
|
||||
|
||||
@@ -197,7 +285,7 @@ def select_threat_by_chain_id(host, process_chain_hash, type):
|
||||
|
||||
|
||||
def update_threat_log(
|
||||
host, risk_score, hit_rule_json, process_chain_hash, raw_json, type, is_end
|
||||
host, risk_score, hit_rule_json, attck_hit_list_json, process_chain_hash, raw_json, type, is_end
|
||||
):
|
||||
global g_threat_table
|
||||
global g_engine
|
||||
@@ -207,6 +295,7 @@ def update_threat_log(
|
||||
.values(
|
||||
risk_score=risk_score,
|
||||
hit_rule=hit_rule_json,
|
||||
attck_hit_list=attck_hit_list_json,
|
||||
data=raw_json,
|
||||
is_end=int(is_end),
|
||||
)
|
||||
@@ -217,6 +306,7 @@ def update_threat_log(
|
||||
)
|
||||
)
|
||||
result = conn.execute(update)
|
||||
conn.close()
|
||||
return result
|
||||
|
||||
|
||||
@@ -230,6 +320,7 @@ def handle_threat_log(threat_id, handle_type):
|
||||
.where(g_threat_table.columns.id == int(threat_id))
|
||||
)
|
||||
result = conn.execute(update)
|
||||
conn.close()
|
||||
return result
|
||||
|
||||
|
||||
@@ -238,8 +329,10 @@ def delete_threat(threat_id):
|
||||
global g_engine
|
||||
conn = g_engine.connect()
|
||||
result = conn.execute(
|
||||
delete(g_threat_table).where(g_threat_table.columns.id == int(threat_id))
|
||||
delete(g_threat_table).where(
|
||||
g_threat_table.columns.id == int(threat_id))
|
||||
)
|
||||
conn.close()
|
||||
return result
|
||||
|
||||
|
||||
@@ -251,6 +344,22 @@ def query_one_threat(threat_id):
|
||||
return threat
|
||||
|
||||
|
||||
def query_raw_host_log_num(host):
|
||||
global g_rawdata_table
|
||||
sql_session = sessionmaker(bind=g_engine)
|
||||
num = sql_session().query(g_rawdata_table).filter_by(host=host).count()
|
||||
sql_session().close()
|
||||
return num
|
||||
|
||||
|
||||
def query_threat_all_num():
|
||||
global g_threat_table
|
||||
sql_session = sessionmaker(bind=g_engine)
|
||||
threat = sql_session().query(g_threat_table).count()
|
||||
sql_session().close()
|
||||
return threat
|
||||
|
||||
|
||||
def query_all_threat_log(query_type):
|
||||
global g_threat_table
|
||||
sql_session = sessionmaker(bind=g_engine)
|
||||
@@ -269,6 +378,7 @@ def query_all_threat_log(query_type):
|
||||
threat_log.is_end,
|
||||
threat_log.start_process_info,
|
||||
threat_log.handle_type,
|
||||
threat_log.attck_hit_list,
|
||||
)
|
||||
.all()
|
||||
)
|
||||
@@ -287,6 +397,7 @@ def query_all_threat_log(query_type):
|
||||
threat_log.is_end,
|
||||
threat_log.start_process_info,
|
||||
threat_log.handle_type,
|
||||
threat_log.attck_hit_list
|
||||
)
|
||||
.filter_by(handle_type=query_type)
|
||||
.all()
|
||||
@@ -299,6 +410,7 @@ def push_threat_log(
|
||||
host,
|
||||
risk_score,
|
||||
hit_rule_json,
|
||||
attck_hit_list_json,
|
||||
process_chain_hash,
|
||||
raw_json,
|
||||
type,
|
||||
@@ -312,6 +424,7 @@ def push_threat_log(
|
||||
risk_score=risk_score,
|
||||
process_chain_hash=process_chain_hash,
|
||||
hit_rule=hit_rule_json,
|
||||
attck_hit_list=attck_hit_list_json,
|
||||
type=type,
|
||||
data=raw_json,
|
||||
timestamp=int(round(time.time() * 1000)),
|
||||
@@ -323,5 +436,6 @@ def push_threat_log(
|
||||
conn = g_engine.connect()
|
||||
# 执行语句
|
||||
result = conn.execute(ins)
|
||||
conn.close()
|
||||
# print(raw_json)
|
||||
return result
|
||||
|
||||
66
Server/statistics.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import time
|
||||
import sql
|
||||
|
||||
all_log_num = 0
|
||||
host_list = {}
|
||||
last_update_time = 0
|
||||
|
||||
|
||||
def get_host_list():
|
||||
global host_list
|
||||
return host_list
|
||||
|
||||
|
||||
def update_host_list(host):
|
||||
global host_list
|
||||
host_list[host] = 1
|
||||
|
||||
|
||||
def update_loged_num(host):
|
||||
global all_log_num
|
||||
global host_list
|
||||
global last_update_time
|
||||
|
||||
all_log_num += 1
|
||||
if host not in host_list:
|
||||
host_list[host] = {
|
||||
'last_update_time': time.time(),
|
||||
'log_num': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
'all_log_num': 0
|
||||
}
|
||||
host_list[host]['all_log_num'] += 1
|
||||
if time.time() - host_list[host]['last_update_time'] > 60:
|
||||
if host_list[host]['all_log_num'] == 0:
|
||||
del host_list[host]
|
||||
host_list[host]['last_update_time'] = time.time()
|
||||
host_list[host]['log_num'].append(host_list[host]['all_log_num'])
|
||||
host_list[host]['all_log_num'] = 0
|
||||
if len(host_list[host]['log_num']) > 10:
|
||||
del host_list[host]['log_num'][0]
|
||||
|
||||
|
||||
def get_loged_num():
|
||||
global all_log_num
|
||||
if all_log_num > 30000000:
|
||||
all_log_num = 0
|
||||
return all_log_num
|
||||
|
||||
|
||||
def get_threat_nums():
|
||||
# sqlite的count啥的还不如自己查出来自己统计
|
||||
host_list = get_host_list()
|
||||
# 懒得做了...
|
||||
# last_logs = sql.query_last_raw_process_log(10)
|
||||
# for iter in last_logs:
|
||||
# print(last_logs)
|
||||
threat_datas = sql.query_all_threat_log(-1)
|
||||
return_data = {"all": len(threat_datas), "confirm": 0,
|
||||
"ingore": 0, "working": 0, "all_log_num": get_loged_num(), "host_list": host_list}
|
||||
for iter in threat_datas:
|
||||
if iter[9] == 1:
|
||||
return_data["confirm"] += 1
|
||||
elif iter[9] == 2:
|
||||
return_data["ingore"] += 1
|
||||
if iter[7] == 0:
|
||||
return_data["working"] += 1
|
||||
return return_data
|
||||
@@ -1 +1 @@
|
||||
.menu-active{background:#f2c037;color:#fff}::-webkit-scrollbar{height:4px;width:5px}::-webkit-scrollbar-thumb{background:#027be3}::-webkit-scrollbar-thumb,::-webkit-scrollbar-track{border-radius:15px;-webkit-box-shadow:inset 0 0 5px #0003}::-webkit-scrollbar-track{background:#ededed}
|
||||
.menu-active{background:#f2c037;color:#fff}::-webkit-scrollbar{height:4px;width:5px}::-webkit-scrollbar-thumb{background:#2f2b30}::-webkit-scrollbar-thumb,::-webkit-scrollbar-track{border-radius:15px;-webkit-box-shadow:inset 0 0 5px #0003}::-webkit-scrollbar-track{background:#ededed}
|
||||
@@ -1 +1 @@
|
||||
<!DOCTYPE html><html><head><title>Duck Sys Eye</title><meta charset=utf-8><meta name=description content=syseye><meta name=format-detection content="telephone=no"><meta name=msapplication-tap-highlight content=no><meta name=viewport content="user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1,width=device-width"><link rel=icon type=image/png sizes=128x128 href=icons/favicon-128x128.png><link rel=icon type=image/png sizes=96x96 href=icons/favicon-96x96.png><link rel=icon type=image/png sizes=32x32 href=icons/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=icons/favicon-16x16.png><link rel=icon type=image/ico href=favicon.ico><script defer src=js/vendor.070221f5.js></script><script defer src=js/app.3ea8aeff.js></script><link href=css/vendor.5b8581f0.css rel=stylesheet><link href=css/app.31d6cfe0.css rel=stylesheet></head><body><div id=q-app></div></body></html>
|
||||
<!DOCTYPE html><html><head><title>Duck Sys Eye</title><meta charset=utf-8><meta name=description content=syseye><meta name=format-detection content="telephone=no"><meta name=msapplication-tap-highlight content=no><meta name=viewport content="user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1,width=device-width"><link rel=icon type=image/png sizes=128x128 href=icons/favicon-128x128.png><link rel=icon type=image/png sizes=96x96 href=icons/favicon-96x96.png><link rel=icon type=image/png sizes=32x32 href=icons/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=icons/favicon-16x16.png><link rel=icon type=image/ico href=favicon.ico><script defer src=js/vendor.ee689f15.js></script><script defer src=js/app.563fee9e.js></script><link href=css/vendor.5b8581f0.css rel=stylesheet><link href=css/app.31d6cfe0.css rel=stylesheet></head><body><div id=q-app></div></body></html>
|
||||
@@ -1 +1 @@
|
||||
"use strict";(globalThis["webpackChunksyseye"]=globalThis["webpackChunksyseye"]||[]).push([[193],{2193:(e,t,s)=>{s.r(t),s.d(t,{default:()=>p});var l=s(3673);const n={class:"fullscreen bg-blue text-white text-center q-pa-md flex flex-center"},o=(0,l._)("div",{style:{"font-size":"30vh"}}," 404 ",-1),c=(0,l._)("div",{class:"text-h2",style:{opacity:".4"}}," Oops. Nothing here... ",-1);function a(e,t,s,a,r,i){const u=(0,l.up)("q-btn");return(0,l.wg)(),(0,l.iD)("div",n,[(0,l._)("div",null,[o,c,(0,l.Wm)(u,{class:"q-mt-xl",color:"white","text-color":"blue",unelevated:"",to:"/",label:"Go Home","no-caps":""})])])}const r=(0,l.aZ)({name:"Error404"});var i=s(4260),u=s(9400),h=s(7518),b=s.n(h);const d=(0,i.Z)(r,[["render",a]]),p=d;b()(r,"components",{QBtn:u.Z})}}]);
|
||||
"use strict";(globalThis["webpackChunksyseye"]=globalThis["webpackChunksyseye"]||[]).push([[193],{2193:(e,t,s)=>{s.r(t),s.d(t,{default:()=>p});var l=s(3673);const n={class:"fullscreen bg-blue text-white text-center q-pa-md flex flex-center"},o=(0,l._)("div",{style:{"font-size":"30vh"}}," 404 ",-1),c=(0,l._)("div",{class:"text-h2",style:{opacity:".4"}}," Oops. Nothing here... ",-1);function a(e,t,s,a,r,i){const u=(0,l.up)("q-btn");return(0,l.wg)(),(0,l.iD)("div",n,[(0,l._)("div",null,[o,c,(0,l.Wm)(u,{class:"q-mt-xl",color:"white","text-color":"blue",unelevated:"",to:"/",label:"Go Home","no-caps":""})])])}const r=(0,l.aZ)({name:"Error404"});var i=s(4260),u=s(8240),h=s(7518),b=s.n(h);const d=(0,i.Z)(r,[["render",a]]),p=d;b()(r,"components",{QBtn:u.Z})}}]);
|
||||
1
Server/templates/js/219.b83c2e16.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(globalThis["webpackChunksyseye"]=globalThis["webpackChunksyseye"]||[]).push([[219],{4219:(a,e,t)=>{t.r(e),t.d(e,{default:()=>_});var n=t(3673),s=t(2323);function o(a,e,t,o,i,l){const r=(0,n.up)("q-td"),p=(0,n.up)("q-btn"),d=(0,n.up)("q-tr"),h=(0,n.up)("q-table");return(0,n.wg)(),(0,n.j4)(h,{class:"q-pa-lg",dense:a.$q.screen.lt.md,title:"白名单列表",columns:a.data_columns,rows:a.data_columns_data,loading:a.loading,pagination:a.pagination,"onUpdate:pagination":e[0]||(e[0]=e=>a.pagination=e),onRequest:a.onRequest},{body:(0,n.w5)((e=>[(0,n.Wm)(d,{props:e},{default:(0,n.w5)((()=>[(0,n.Wm)(r,{key:"path",props:e},{default:(0,n.w5)((()=>[(0,n.Uk)((0,s.zw)(e.row.path),1)])),_:2},1032,["props"]),(0,n.Wm)(r,{key:"hash",props:e},{default:(0,n.w5)((()=>[(0,n.Uk)((0,s.zw)(e.row.hash),1)])),_:2},1032,["props"]),(0,n.Wm)(r,{key:"reason",props:e},{default:(0,n.w5)((()=>[(0,n.Uk)((0,s.zw)(e.row.reason),1)])),_:2},1032,["props"]),(0,n.Wm)(r,{key:"timestamp",props:e},{default:(0,n.w5)((()=>[(0,n.Uk)((0,s.zw)(a.time_parase(e.row.timestamp)),1)])),_:2},1032,["props"]),(0,n.Wm)(r,{key:"action",props:e},{default:(0,n.w5)((()=>[(0,n.Wm)(p,{color:"red",label:"移除白名单",onClick:t=>a.delete_white_hash(e.row.hash)},null,8,["onClick"])])),_:2},1032,["props"])])),_:2},1032,["props"])])),_:1},8,["dense","columns","rows","loading","pagination","onRequest"])}var i=t(52),l=t.n(i);const r=(0,n.aZ)({name:"WhiteList",data:function(){return{data_columns:[{name:"path",align:"center",label:"路径",field:"path"},{name:"hash",align:"center",label:"hash",field:"hash"},{name:"reason",align:"center",label:"原因",field:"reason"},{name:"timestamp",align:"center",label:"时间",field:"timestamp"},{name:"action",align:"center",label:"操作",field:"steamid"}],data_columns_data:[],loading:!1,pagination:{sortBy:"desc",descending:!1,page:1,rowsPerPage:10,rowsNumber:10}}},mounted(){this.onRequest({pagination:this.pagination,filter:void 0})},methods:{delete_white_hash(a){l().get("/api/v1/del/white_list?hash="+a).then((a=>{this.onRequest({pagination:this.pagination,filter:void 0})}))},time_parase(a){const e=a=>a<10?"0"+a:a,t=new Date(Number(a));console.log("time",a);const n=t.getFullYear(),s=t.getMonth()+1,o=t.getDate(),i=t.getHours(),l=t.getMinutes(),r=t.getSeconds();return n+"-"+e(s)+"-"+e(o)+" "+e(i)+":"+e(l)+":"+e(r)},onRequest(a){this.data_columns_data=[],this.loading=!0;const{page:e}=a.pagination;l().get("/api/v1/query/white_list_all").then((a=>{const t=a.data.result;console.log(t);for(let e=0;e<t.length;e++){const a=t[e];this.data_columns_data.push(a)}this.pagination.page=e,this.pagination.rowsNumber=this.data_columns_data.length,this.pagination.rowsPerPage=this.data_columns_data.length,this.loading=!1}))}}});var p=t(4260),d=t(7898),h=t(8186),g=t(3884),u=t(8240),m=t(7518),c=t.n(m);const w=(0,p.Z)(r,[["render",o]]),_=w;c()(r,"components",{QTable:d.Z,QTr:h.Z,QTd:g.Z,QBtn:u.Z})}}]);
|
||||
1
Server/templates/js/550.c577ffe7.js
Normal file
BIN
Server/templates/js/550.c577ffe7.js.gz
Normal file
1
Server/templates/js/773.945120ec.js
Normal file
@@ -1 +0,0 @@
|
||||
"use strict";(globalThis["webpackChunksyseye"]=globalThis["webpackChunksyseye"]||[]).push([[904],{6904:(s,a,e)=>{e.r(a),e.d(a,{default:()=>h});var r=e(3673);const n={class:"row q-gutter-md q-mb-sm q-pa-lg"};function t(s,a,e,t,c,o){return(0,r.wg)(),(0,r.iD)("h4",n,"施工中....")}const c=(0,r.aZ)({name:"Dashboard"});var o=e(4260);const u=(0,o.Z)(c,[["render",t]]),h=u}}]);
|
||||
1
Server/templates/js/95.8b297ab2.js
Normal file
@@ -1 +0,0 @@
|
||||
"use strict";(globalThis["webpackChunksyseye"]=globalThis["webpackChunksyseye"]||[]).push([[950],{950:(e,a,t)=>{t.r(a),t.d(a,{default:()=>T});var l=t(3673);const r=(0,l.Uk)(" DuckSysEye内部测试版本v0.0.0.1 "),i=(0,l.Uk)(" 仪表盘 "),n=(0,l.Uk)(" 未处理威胁列表 "),o=(0,l.Uk)(" 已处理威胁列表 "),u=(0,l.Uk)(" 已忽略威胁列表 ");function c(e,a,t,c,s,d){const m=(0,l.up)("q-toolbar-title"),w=(0,l.up)("q-btn"),p=(0,l.up)("q-toolbar"),b=(0,l.up)("q-header"),_=(0,l.up)("q-icon"),h=(0,l.up)("q-item-section"),v=(0,l.up)("q-item"),f=(0,l.up)("q-list"),W=(0,l.up)("q-scroll-area"),k=(0,l.up)("q-drawer"),g=(0,l.up)("router-view"),y=(0,l.up)("q-page-container"),L=(0,l.up)("q-layout"),Z=(0,l.Q2)("ripple");return(0,l.wg)(),(0,l.j4)(L,{view:"lHh Lpr lFf",style:{"background-color":"rgb(239, 243, 246)"}},{default:(0,l.w5)((()=>[(0,l.Wm)(b,{elevated:"","height-hint":"98"},{default:(0,l.w5)((()=>[(0,l.Wm)(p,{class:"text-primary bg-white"},{default:(0,l.w5)((()=>[(0,l.Wm)(m,null,{default:(0,l.w5)((()=>[r])),_:1}),(0,l.Wm)(w,{flat:"",round:"",dense:"",icon:"more_vert"})])),_:1})])),_:1}),(0,l.Wm)(k,{"show-if-above":"",mini:e.miniState,onMouseover:a[4]||(a[4]=a=>e.miniState=!1),onMouseout:a[5]||(a[5]=a=>e.miniState=!0),width:200,breakpoint:500,bordered:"",class:"bg-white text-primary"},{default:(0,l.w5)((()=>[(0,l.Wm)(W,{class:"fit"},{default:(0,l.w5)((()=>[(0,l.Wm)(f,{padding:""},{default:(0,l.w5)((()=>[(0,l.wy)(((0,l.wg)(),(0,l.j4)(v,{active:"dashboard"==e.selectLabel,clickable:"","active-class":"menu-active",onClick:a[0]||(a[0]=a=>e.selectLabel="dashboard"),to:"/page/dashboard"},{default:(0,l.w5)((()=>[(0,l.Wm)(h,{avatar:""},{default:(0,l.w5)((()=>[(0,l.Wm)(_,{name:"dashboard"})])),_:1}),(0,l.Wm)(h,null,{default:(0,l.w5)((()=>[i])),_:1})])),_:1},8,["active"])),[[Z]]),(0,l.wy)(((0,l.wg)(),(0,l.j4)(v,{active:"non_hanlde_report"==e.selectLabel,clickable:"","active-class":"menu-active",onClick:a[1]||(a[1]=a=>{e.selectLabel="non_hanlde_report",e.routerToThreatList(0)})},{default:(0,l.w5)((()=>[(0,l.Wm)(h,{avatar:""},{default:(0,l.w5)((()=>[(0,l.Wm)(_,{name:"report"})])),_:1}),(0,l.Wm)(h,null,{default:(0,l.w5)((()=>[n])),_:1})])),_:1},8,["active"])),[[Z]]),(0,l.wy)(((0,l.wg)(),(0,l.j4)(v,{active:"handle_report"==e.selectLabel,clickable:"","active-class":"menu-active",onClick:a[2]||(a[2]=a=>{e.selectLabel="handle_report",e.routerToThreatList(1)})},{default:(0,l.w5)((()=>[(0,l.Wm)(h,{avatar:""},{default:(0,l.w5)((()=>[(0,l.Wm)(_,{name:"done"})])),_:1}),(0,l.Wm)(h,null,{default:(0,l.w5)((()=>[o])),_:1})])),_:1},8,["active"])),[[Z]]),(0,l.wy)(((0,l.wg)(),(0,l.j4)(v,{active:"ingore_report"==e.selectLabel,clickable:"","active-class":"menu-active",onClick:a[3]||(a[3]=a=>{e.selectLabel="ingore_report",e.routerToThreatList(2)})},{default:(0,l.w5)((()=>[(0,l.Wm)(h,{avatar:""},{default:(0,l.w5)((()=>[(0,l.Wm)(_,{name:"texture"})])),_:1}),(0,l.Wm)(h,null,{default:(0,l.w5)((()=>[u])),_:1})])),_:1},8,["active"])),[[Z]])])),_:1})])),_:1})])),_:1},8,["mini"]),(0,l.Wm)(y,null,{default:(0,l.w5)((()=>[(0,l.Wm)(g)])),_:1})])),_:1})}const s=(0,l.aZ)({name:"MainLayout",setup(){return{}},data:function(){return{selectLabel:"non_hanlde_report",drawer:!1,miniState:!0}},methods:{routerToThreatList(e){this.$router.push({name:"index",params:{queryIndex:e}})}}});var d=t(4260),m=t(9214),w=t(3812),p=t(9570),b=t(3747),_=t(9400),h=t(2901),v=t(7704),f=t(7011),W=t(3414),k=t(2035),g=t(4554),y=t(2652),L=t(6489),Z=t(7518),q=t.n(Z);const Q=(0,d.Z)(s,[["render",c]]),T=Q;q()(s,"components",{QLayout:m.Z,QHeader:w.Z,QToolbar:p.Z,QToolbarTitle:b.Z,QBtn:_.Z,QDrawer:h.Z,QScrollArea:v.Z,QList:f.Z,QItem:W.Z,QItemSection:k.Z,QIcon:g.Z,QPageContainer:y.Z}),q()(s,"directives",{Ripple:L.Z})}}]);
|
||||
1
Server/templates/js/app.563fee9e.js
Normal file
32
Server/templates/js/vendor.ee689f15.js
Normal file
BIN
Server/templates/js/vendor.ee689f15.js.gz
Normal file
@@ -1,151 +1,217 @@
|
||||
import logging
|
||||
import hash_white_list
|
||||
import json
|
||||
from flask import Flask
|
||||
from flask import request
|
||||
import sql
|
||||
import log
|
||||
import rule
|
||||
import config
|
||||
from flask import Flask, render_template, request
|
||||
import plugin
|
||||
import logging
|
||||
app = Flask(__name__,
|
||||
template_folder="./templates",
|
||||
static_folder="./templates",
|
||||
static_url_path="")
|
||||
app.jinja_env.variable_start_string = '{.<'
|
||||
app.jinja_env.variable_end_string = '>.}'
|
||||
import html
|
||||
import rule
|
||||
import statistics
|
||||
app = Flask(
|
||||
__name__,
|
||||
template_folder="./templates",
|
||||
static_folder="./templates",
|
||||
static_url_path="",
|
||||
)
|
||||
app.jinja_env.variable_start_string = "{.<"
|
||||
app.jinja_env.variable_end_string = ">.}"
|
||||
|
||||
|
||||
@app.route('/')
|
||||
@app.route("/")
|
||||
def root():
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP:
|
||||
return "Access Denied"
|
||||
return render_template("index.html")
|
||||
|
||||
|
||||
@app.route('/static/<path:path>')
|
||||
@app.route("/static/<path:path>")
|
||||
def on_vue_static(path):
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP:
|
||||
return "Access Denied"
|
||||
return app.send_static_file("./" + path)
|
||||
|
||||
|
||||
@app.route('/plugin/<path:path>')
|
||||
@app.route("/plugin/<path:path>")
|
||||
def on_plugin_access(path):
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP:
|
||||
return "Access Denied"
|
||||
return plugin.dispath_html_draw(path)
|
||||
|
||||
|
||||
@app.route('/api/v1/get/plugin_menu')
|
||||
@app.route("/api/v1/get/plugin_menu")
|
||||
def plugin_menu():
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP:
|
||||
return "Access Denied"
|
||||
return {'data': {'menu': plugin.dispath_html_menu()}}
|
||||
return {"data": {"menu": plugin.dispath_html_menu()}}
|
||||
|
||||
|
||||
@app.route('/api/v1/get/threat_statistics', methods=['GET'])
|
||||
@app.route("/api/v1/get/threat_statistics", methods=["GET"])
|
||||
def threat_statistics():
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP:
|
||||
return "Access Denied"
|
||||
# sqlite的count啥的还不如自己查出来自己统计
|
||||
threat_datas = sql.query_all_threat_log(-1)
|
||||
return_data = {
|
||||
'all': len(threat_datas),
|
||||
'confirm': 0,
|
||||
'ingore': 0,
|
||||
'working': 0
|
||||
}
|
||||
for iter in threat_datas:
|
||||
if iter[9] == 1:
|
||||
return_data['confirm'] += 1
|
||||
elif iter[9] == 2:
|
||||
return_data['ingore'] += 1
|
||||
if iter[7] == 0:
|
||||
return_data['working'] += 1
|
||||
return {'data': return_data}
|
||||
return {"data": statistics.get_threat_nums()}
|
||||
|
||||
|
||||
@app.route('/api/v1/get/process_chain/handle', methods=['GET'])
|
||||
@app.route("/api/v1/query/white_list_all", methods=["GET"])
|
||||
def white_list_query_all():
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP:
|
||||
return "Access Denied"
|
||||
all_list = sql.query_all_white_list()
|
||||
result = []
|
||||
for iter in all_list:
|
||||
result.append({
|
||||
"hash": iter[1],
|
||||
"path": iter[2],
|
||||
"timestamp": iter[3],
|
||||
"reason": iter[4]
|
||||
})
|
||||
return {"status": "success", "result": result}
|
||||
|
||||
|
||||
@app.route("/api/v1/query/white_list", methods=["GET"])
|
||||
def white_list_query():
|
||||
hash = request.args.get("hash")
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP or hash is None or len(hash) == 0:
|
||||
return "Access Denied"
|
||||
hash = hash.lower()
|
||||
result = 0
|
||||
if hash in hash_white_list.g_white_list:
|
||||
result = 1
|
||||
return {"status": "success", "result": result}
|
||||
|
||||
|
||||
@app.route("/api/v1/del/white_list", methods=["GET"])
|
||||
def white_list_del():
|
||||
hash = request.args.get("hash")
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP or hash is None or len(hash) == 0:
|
||||
return "Access Denied"
|
||||
hash = hash.lower()
|
||||
if hash in hash_white_list.g_white_list:
|
||||
sql.delete_white_list(hash)
|
||||
hash_white_list.g_white_list.remove(hash)
|
||||
return {"status": "success"}
|
||||
|
||||
|
||||
@app.route("/api/v1/set/white_list", methods=["POST"])
|
||||
def white_list_set():
|
||||
body_data = request.data.decode()
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP:
|
||||
return "Access Denied"
|
||||
json_data = json.loads(body_data)
|
||||
hash = html.escape(json_data["hash"]).lower()
|
||||
path = html.escape(json_data["path"]).lower()
|
||||
reason = html.escape(json_data["reason"])
|
||||
hash_white_list.add_white_list(path, hash, reason)
|
||||
return {"status": "success"}
|
||||
|
||||
|
||||
@app.route("/api/v1/get/process_chain/handle", methods=["GET"])
|
||||
def handle_chain_data():
|
||||
id = request.args.get('id')
|
||||
handletype = request.args.get('handletype')
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP or (id is None or handletype is None):
|
||||
id = request.args.get("id")
|
||||
handletype = request.args.get("handletype")
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP or (
|
||||
id is None or handletype is None
|
||||
):
|
||||
return "Access Denied"
|
||||
sql.handle_threat_log(id, handletype)
|
||||
return {'data': {'success': 1}}
|
||||
return {"data": {"success": 1}}
|
||||
|
||||
|
||||
@app.route('/api/v1/get/process_chain/delete', methods=['GET'])
|
||||
@app.route("/api/v1/get/process_chain/delete", methods=["GET"])
|
||||
def delete_chain_data():
|
||||
id = request.args.get('id')
|
||||
id = request.args.get("id")
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP or id is None:
|
||||
return "Access Denied"
|
||||
sql.delete_threat(id)
|
||||
return {'data': {'success': 1}}
|
||||
return {"data": {"success": 1}}
|
||||
|
||||
|
||||
@app.route('/api/v1/get/process_chain/pull', methods=['GET'])
|
||||
@app.route("/api/v1/get/process_chain/pull", methods=["GET"])
|
||||
def pull_chain_data():
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP:
|
||||
return "Access Denied"
|
||||
id = request.args.get('id')
|
||||
id = request.args.get("id")
|
||||
return_data = {}
|
||||
if id is not None:
|
||||
threat_data = sql.query_one_threat(id)
|
||||
return_data = {
|
||||
'host': threat_data[1],
|
||||
'chain_hash': threat_data[2],
|
||||
'type': threat_data[3],
|
||||
'risk_score': threat_data[4],
|
||||
'hit_rule': json.loads(threat_data[5]),
|
||||
'chain': json.loads(threat_data[6]),
|
||||
'is_end': threat_data[7]
|
||||
"host": threat_data[1],
|
||||
"chain_hash": threat_data[2],
|
||||
"type": threat_data[3],
|
||||
"risk_score": threat_data[4],
|
||||
"hit_rule": json.loads(threat_data[5]),
|
||||
"hit_attck": json.loads(threat_data[6]),
|
||||
"chain": json.loads(threat_data[7]),
|
||||
"is_end": threat_data[8],
|
||||
}
|
||||
return {'data': return_data}
|
||||
return {"data": return_data}
|
||||
|
||||
|
||||
@app.route('/api/v1/get/process_chain/all')
|
||||
@app.route("/api/v1/get/process_chain/all")
|
||||
def process_chain():
|
||||
# -1全部 0未处理的 1处理的 2忽略的
|
||||
query_type = request.args.get('query_type')
|
||||
query_type = request.args.get("query_type")
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP or query_type is None:
|
||||
return "Access Denied"
|
||||
threat_datas = sql.query_all_threat_log(query_type)
|
||||
return_data = []
|
||||
for iter in threat_datas:
|
||||
return_data.append({
|
||||
'host': iter[0],
|
||||
'chain_hash': iter[1],
|
||||
'hit_rule': json.loads(iter[2]),
|
||||
'time': iter[3],
|
||||
'type': iter[4],
|
||||
'risk_score': iter[5],
|
||||
'id': iter[6],
|
||||
'is_end': iter[7],
|
||||
'start_process': json.loads(iter[8]),
|
||||
})
|
||||
return {'data': return_data}
|
||||
return_data.append(
|
||||
{
|
||||
"host": iter[0],
|
||||
"chain_hash": iter[1],
|
||||
"hit_rule": json.loads(iter[2]),
|
||||
"time": iter[3],
|
||||
"type": iter[4],
|
||||
"risk_score": iter[5],
|
||||
"id": iter[6],
|
||||
"is_end": iter[7],
|
||||
"start_process": json.loads(iter[8]),
|
||||
"attck_hit_list": json.loads(iter[10]),
|
||||
}
|
||||
)
|
||||
return {"data": return_data}
|
||||
|
||||
|
||||
@app.route('/api/v1/process', methods=['POST'])
|
||||
@app.route("/api/v1/process", methods=["POST"])
|
||||
def process():
|
||||
if request.method == 'POST':
|
||||
if request.method == "POST":
|
||||
# print(request.data)
|
||||
body_data = request.data.decode()
|
||||
# 转小写
|
||||
host = request.remote_addr
|
||||
log.process_log(host, json.loads(body_data.lower()), body_data)
|
||||
statistics.update_loged_num(host)
|
||||
|
||||
return {'status': 'success'}
|
||||
return {"status": "success"}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@ app.route("/api/v1/log_hunt", methods=["POST"])
|
||||
def log_rescan():
|
||||
if request.remote_addr not in config.ALLOW_ACCESS_IP:
|
||||
return "Access Denied"
|
||||
start_time = request.args.get("start_time")
|
||||
end_time = request.args.get("end_time")
|
||||
raw_logs = sql.select_process_raw_log_by_time(
|
||||
int(start_time), int(end_time))
|
||||
threat_data = log.process_raw_log(raw_logs)
|
||||
return {"data": threat_data}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
plugin.reload_plugs()
|
||||
sql.init()
|
||||
rule.init_rule()
|
||||
hash_white_list.synchronization_white_list()
|
||||
|
||||
# 如果你觉得日志太多了,去掉这个注释...
|
||||
flask_log = logging.getLogger('werkzeug')
|
||||
flask_log = logging.getLogger("werkzeug")
|
||||
flask_log.setLevel(logging.ERROR)
|
||||
print("注意,你正在使用测试版,请随时关注github以获取最新版本:")
|
||||
print("https://github.com/RoomaSec/RmEye")
|
||||
# statistics.get_threat_nums()
|
||||
app.run(debug=True, host="0.0.0.0")
|
||||
|
||||
5
Web/syseye/package-lock.json
generated
@@ -5349,6 +5349,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"js-base64": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz",
|
||||
"integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ=="
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"axios": "^0.27.2",
|
||||
"core-js": "^3.6.5",
|
||||
"echarts": "^5.3.3",
|
||||
"js-base64": "^3.7.2",
|
||||
"quasar": "^2.0.0",
|
||||
"vue": "^3.0.0",
|
||||
"vue-router": "^4.0.0"
|
||||
|
||||
117
Web/syseye/src/assets/b64.js
Normal file
@@ -0,0 +1,117 @@
|
||||
/* eslint-disable camelcase */
|
||||
/* eslint-disable eqeqeq */
|
||||
/* eslint-disable no-useless-escape */
|
||||
// 1.加密解密方法使用:
|
||||
|
||||
// 1.加密
|
||||
// var str = '124中文内容';
|
||||
// var base = new Base64();
|
||||
// var result = base.encode(str);
|
||||
// //document.write(result);
|
||||
|
||||
// //2.解密
|
||||
// var result2 = base.decode(result);
|
||||
// document.write(result2);
|
||||
// //2.加密、解密算法封装:
|
||||
|
||||
function Base64 () {
|
||||
// private property
|
||||
const _keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
|
||||
|
||||
// public method for encoding
|
||||
this.encode = function (input) {
|
||||
let output = ''
|
||||
let chr1, chr2, chr3, enc1, enc2, enc3, enc4
|
||||
let i = 0
|
||||
input = _utf8_encode(input)
|
||||
while (i < input.length) {
|
||||
chr1 = input.charCodeAt(i++)
|
||||
chr2 = input.charCodeAt(i++)
|
||||
chr3 = input.charCodeAt(i++)
|
||||
enc1 = chr1 >> 2
|
||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4)
|
||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6)
|
||||
enc4 = chr3 & 63
|
||||
if (isNaN(chr2)) {
|
||||
enc3 = enc4 = 64
|
||||
} else if (isNaN(chr3)) {
|
||||
enc4 = 64
|
||||
}
|
||||
output = output +
|
||||
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
|
||||
_keyStr.charAt(enc3) + _keyStr.charAt(enc4)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// public method for decoding
|
||||
this.decode = function (input) {
|
||||
let output = ''
|
||||
let chr1, chr2, chr3
|
||||
let enc1, enc2, enc3, enc4
|
||||
let i = 0
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '')
|
||||
while (i < input.length) {
|
||||
enc1 = _keyStr.indexOf(input.charAt(i++))
|
||||
enc2 = _keyStr.indexOf(input.charAt(i++))
|
||||
enc3 = _keyStr.indexOf(input.charAt(i++))
|
||||
enc4 = _keyStr.indexOf(input.charAt(i++))
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4)
|
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2)
|
||||
chr3 = ((enc3 & 3) << 6) | enc4
|
||||
output = output + String.fromCharCode(chr1)
|
||||
if (enc3 != 64) {
|
||||
output = output + String.fromCharCode(chr2)
|
||||
}
|
||||
if (enc4 != 64) {
|
||||
output = output + String.fromCharCode(chr3)
|
||||
}
|
||||
}
|
||||
output = _utf8_decode(output)
|
||||
return output
|
||||
}
|
||||
|
||||
// private method for UTF-8 encoding
|
||||
var _utf8_encode = function (string) {
|
||||
string = string.replace(/\r\n/g, '\n')
|
||||
let utftext = ''
|
||||
for (let n = 0; n < string.length; n++) {
|
||||
const c = string.charCodeAt(n)
|
||||
if (c < 128) {
|
||||
utftext += String.fromCharCode(c)
|
||||
} else if ((c > 127) && (c < 2048)) {
|
||||
utftext += String.fromCharCode((c >> 6) | 192)
|
||||
utftext += String.fromCharCode((c & 63) | 128)
|
||||
} else {
|
||||
utftext += String.fromCharCode((c >> 12) | 224)
|
||||
utftext += String.fromCharCode(((c >> 6) & 63) | 128)
|
||||
utftext += String.fromCharCode((c & 63) | 128)
|
||||
}
|
||||
}
|
||||
return utftext
|
||||
}
|
||||
// private method for UTF-8 decoding
|
||||
var _utf8_decode = function (utftext) {
|
||||
let string = ''
|
||||
let i = 0
|
||||
let c = 0, c1 = 0, c2 = 0
|
||||
while (i < utftext.length) {
|
||||
c = utftext.charCodeAt(i)
|
||||
if (c < 128) {
|
||||
string += String.fromCharCode(c)
|
||||
i++
|
||||
} else if ((c > 191) && (c < 224)) {
|
||||
c2 = utftext.charCodeAt(i + 1)
|
||||
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63))
|
||||
i += 2
|
||||
} else {
|
||||
c2 = utftext.charCodeAt(i + 1)
|
||||
c1 = utftext.charCodeAt(i + 2)
|
||||
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c1 & 63))
|
||||
i += 3
|
||||
}
|
||||
}
|
||||
return string
|
||||
}
|
||||
}
|
||||
export default Base64
|
||||
0
Web/syseye/src/css/light.css
Normal file
@@ -1,140 +1,105 @@
|
||||
<template>
|
||||
<q-layout view="lHh Lpr lFf" style="background-color: rgb(239, 243, 246)">
|
||||
<q-layout view="lHh Lpr lFf" :style="`background-color: ${colors.background}`">
|
||||
<q-header elevated height-hint="98">
|
||||
<q-toolbar class="text-primary bg-white">
|
||||
<q-toolbar-title> DuckSysEye内部测试版本v0.0.0.1 </q-toolbar-title>
|
||||
<q-btn flat round dense icon="more_vert"></q-btn>
|
||||
</q-toolbar>
|
||||
<q-toolbar class="text-white" :style="`background-color: ${colors.toolbar}`">
|
||||
<q-toolbar-title> RmEye测试版v1.0.1.3 </q-toolbar-title>
|
||||
<q-btn flat dense icon="restore" label="重置颜色" @click="cleanUpCookie">
|
||||
</q-btn>
|
||||
<q-popup-proxy>
|
||||
<q-banner>
|
||||
<q-color v-model="colors.toolbar" @change="updateCookie(colors.toolbar)" class="my-picker" />
|
||||
</q-banner>
|
||||
</q-popup-proxy>
|
||||
</q-toolbar>
|
||||
<q-toolbar :style="`font-size: 16px;background-color: ${colors.layout}`">
|
||||
<q-breadcrumbs active-color="white">
|
||||
<q-breadcrumbs-el label="仪表盘" icon="dashboard" to="/page/dashboard" />
|
||||
<q-breadcrumbs-el label="未处理威胁列表" icon="report" to="#" @click="routerToThreatList(0);" />
|
||||
<q-breadcrumbs-el label="已处理威胁列表" icon="done" to="#" @click="routerToThreatList(1);" />
|
||||
<q-breadcrumbs-el label="已忽略威胁列表" icon="texture" to="#" @click="routerToThreatList(2);" />
|
||||
<q-breadcrumbs-el label="白名单列表" icon="list" to="#" @click="routerToWhiteList();" />
|
||||
</q-breadcrumbs>
|
||||
|
||||
<q-popup-proxy>
|
||||
<q-banner>
|
||||
<q-color v-model="colors.layout" @change="updateCookie(colors.layout)" class="my-picker" />
|
||||
</q-banner>
|
||||
</q-popup-proxy>
|
||||
</q-toolbar>
|
||||
</q-header>
|
||||
<q-drawer
|
||||
show-if-above
|
||||
:mini="miniState"
|
||||
@mouseover="miniState = false"
|
||||
@mouseout="miniState = true"
|
||||
:width="200"
|
||||
:breakpoint="500"
|
||||
bordered
|
||||
class="bg-white text-primary"
|
||||
>
|
||||
<q-scroll-area class="fit">
|
||||
<q-list padding>
|
||||
<q-item
|
||||
:active="selectLabel == 'dashboard'"
|
||||
clickable
|
||||
v-ripple
|
||||
active-class="menu-active"
|
||||
@click="selectLabel = 'dashboard'"
|
||||
to="/page/dashboard"
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="dashboard" />
|
||||
</q-item-section>
|
||||
<q-item-section> 仪表盘 </q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item
|
||||
:active="selectLabel == 'non_hanlde_report'"
|
||||
clickable
|
||||
v-ripple
|
||||
active-class="menu-active"
|
||||
@click="
|
||||
selectLabel = 'non_hanlde_report';
|
||||
routerToThreatList(0);
|
||||
"
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="report" />
|
||||
</q-item-section>
|
||||
<q-item-section> 未处理威胁列表 </q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
:active="selectLabel == 'handle_report'"
|
||||
clickable
|
||||
v-ripple
|
||||
active-class="menu-active"
|
||||
@click="
|
||||
selectLabel = 'handle_report';
|
||||
routerToThreatList(1);
|
||||
"
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="done" />
|
||||
</q-item-section>
|
||||
<q-item-section> 已处理威胁列表 </q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
:active="selectLabel == 'ingore_report'"
|
||||
clickable
|
||||
v-ripple
|
||||
active-class="menu-active"
|
||||
@click="
|
||||
selectLabel = 'ingore_report';
|
||||
routerToThreatList(2);
|
||||
"
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="texture" />
|
||||
</q-item-section>
|
||||
<q-item-section> 已忽略威胁列表 </q-item-section>
|
||||
</q-item>
|
||||
<template v-for="(item, index) in plugin" v-bind:key="index">
|
||||
<q-item
|
||||
:active="selectLabel == item['name']"
|
||||
clickable
|
||||
v-ripple
|
||||
active-class="menu-active"
|
||||
@click="
|
||||
selectLabel = item['name'];
|
||||
routerToPlugin(item['html']);
|
||||
"
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-icon :name="item['icon']" />
|
||||
</q-item-section>
|
||||
<q-item-section> {{ item["name"] }} </q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-list>
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
|
||||
<template v-if="isInPlugin == false">
|
||||
<q-page-container>
|
||||
<router-view />
|
||||
</q-page-container>
|
||||
<q-page-container>
|
||||
<router-view />
|
||||
</q-page-container>
|
||||
</template>
|
||||
<template v-if="isInPlugin">
|
||||
<div class="q-gutter-md q-mb-sm q-pa-lg">
|
||||
<HtmlPanel v-model:url="PluginUrl" />
|
||||
</div>
|
||||
<div class="q-gutter-md q-mb-sm q-pa-lg">
|
||||
<HtmlPanel v-model:url="PluginUrl" />
|
||||
</div>
|
||||
</template>
|
||||
</q-layout>
|
||||
</q-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
import Base64 from '../assets/b64.js'
|
||||
|
||||
import {
|
||||
defineComponent
|
||||
} from 'vue'
|
||||
import HtmlPanel from '../components/Html.vue' // 根据实际路径导入
|
||||
import axios from 'axios'
|
||||
import {
|
||||
Cookies
|
||||
} from 'quasar'
|
||||
export default defineComponent({
|
||||
components: { HtmlPanel },
|
||||
components: {
|
||||
HtmlPanel
|
||||
},
|
||||
name: 'MainLayout',
|
||||
setup () {
|
||||
return {}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
selectLabel: 'non_hanlde_report',
|
||||
selectLabel: 'dashboard',
|
||||
drawer: false,
|
||||
miniState: true,
|
||||
plugin: [],
|
||||
isInPlugin: false,
|
||||
PluginUrl: ''
|
||||
PluginUrl: '',
|
||||
colors: {
|
||||
layout: 'rgb(47,43,48)',
|
||||
toolbar: 'rgb(210,61,42)',
|
||||
background: 'rgb(239, 243, 246)'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateCookie (selectItem) {
|
||||
const b64Obj = new Base64()
|
||||
Cookies.set('custom_banner', b64Obj.encode(JSON.stringify(this.colors)))
|
||||
},
|
||||
cleanUpCookie () {
|
||||
Cookies.remove('custom_threat_item')
|
||||
Cookies.remove('custom_banner')
|
||||
|
||||
// refesh
|
||||
window.location.reload()
|
||||
},
|
||||
routerToWhiteList () {
|
||||
this.isInPlugin = false
|
||||
this.$router.push({
|
||||
name: 'whitelist'
|
||||
})
|
||||
},
|
||||
routerToThreatList (index) {
|
||||
this.isInPlugin = false
|
||||
this.$router.push({ name: 'index', params: { queryIndex: index } })
|
||||
this.$router.push({
|
||||
name: 'index',
|
||||
params: {
|
||||
queryIndex: index
|
||||
}
|
||||
})
|
||||
},
|
||||
routerToPlugin (url) {
|
||||
this.isInPlugin = true
|
||||
@@ -153,6 +118,11 @@ export default defineComponent({
|
||||
},
|
||||
mounted () {
|
||||
this.getPluginsMenu()
|
||||
const coockieCustomBanner = Cookies.get('custom_banner')
|
||||
if (coockieCustomBanner) {
|
||||
const b64Obj = new Base64()
|
||||
this.colors = JSON.parse(b64Obj.decode(coockieCustomBanner))
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -174,7 +144,7 @@ export default defineComponent({
|
||||
/*滚动条里面小方块*/
|
||||
border-radius: 15px;
|
||||
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
|
||||
background: #027be3;
|
||||
background: rgb(47,43,48);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
|
||||
@@ -1,10 +1,220 @@
|
||||
<template>
|
||||
<h4 class="row q-gutter-md q-mb-sm q-pa-lg">施工中....</h4>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
<div class="q-gutter-md q-mb-sm q-pa-lg">
|
||||
<div>
|
||||
<q-card class="bg-transparent no-shadow no-border">
|
||||
<q-card-section class="q-pa-none">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div v-for="(item, index) in Threatitems" :key="index" class="col-md-3 col-sm-12 col-xs-12">
|
||||
<q-item :style="`background-color: ${item.color1}`" class="q-pa-none">
|
||||
<q-item-section side :style="`background-color: ${item.color2}`" class="q-pa-lg q-mr-none text-white">
|
||||
<q-icon :name="item.icon" color="white" size="24px"></q-icon>
|
||||
</q-item-section>
|
||||
<q-item-section class="q-pa-md q-ml-none text-white">
|
||||
<q-item-label class="text-white text-h6 text-weight-bolder">{{
|
||||
item.value
|
||||
}}</q-item-label>
|
||||
<q-item-label>{{ item.title }}</q-item-label>
|
||||
<q-popup-proxy>
|
||||
<q-banner>
|
||||
<q-color v-model="item.color1" @change="updateCookie(Threatitems[index].color1)" class="my-picker" />
|
||||
</q-banner>
|
||||
</q-popup-proxy>
|
||||
</q-item-section>
|
||||
<q-popup-proxy>
|
||||
<q-banner>
|
||||
<q-color v-model="item.color2" @change="updateCookie(Threatitems[index].color2)" class="my-picker" />
|
||||
</q-banner>
|
||||
</q-popup-proxy>
|
||||
</q-item>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
<q-card class="no-shadow" style="background: rbg(255,255,255)">
|
||||
<q-card-section>
|
||||
<div class="text-h6">
|
||||
主机数量: {{threatStatistics.host_num}}/50 <q-icon name="info" class="text-brown cursor-pointer">
|
||||
<q-popup-proxy transition-show="flip-up" transition-hide="flip-down">
|
||||
<q-banner class="bg-brown text-white">
|
||||
<template v-slot:avatar>
|
||||
<q-icon name="lightbulb" />
|
||||
</template>
|
||||
由于python+sqlite数据库作为后端,理论上最高支持的主机数量为50.
|
||||
</q-banner>
|
||||
</q-popup-proxy>
|
||||
</q-icon>
|
||||
|
||||
</div>
|
||||
<div class="text-subtitle2">最近日志数量: {{threatStatistics.all_log_num}}</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<div ref="main_draw" style="width: 100%; height: 600px; ">
|
||||
1
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Base64 from '../assets/b64.js'
|
||||
|
||||
import {
|
||||
defineComponent
|
||||
} from 'vue'
|
||||
import axios from 'axios'
|
||||
import {
|
||||
Cookies
|
||||
} from 'quasar'
|
||||
import * as echarts from 'echarts'
|
||||
export default defineComponent({
|
||||
name: 'Dashboard'
|
||||
name: 'Dashboard',
|
||||
data () {
|
||||
return {
|
||||
Threatitems: [{
|
||||
title: '发现的威胁',
|
||||
icon: 'remove_red_eye',
|
||||
value: '200',
|
||||
color1: '#EE9B00',
|
||||
color2: '#EE9B00'
|
||||
},
|
||||
{
|
||||
title: '确认的威胁',
|
||||
icon: 'flash_on',
|
||||
value: '500',
|
||||
color1: '#CA6702',
|
||||
color2: '#CA6702'
|
||||
},
|
||||
{
|
||||
title: '忽略的威胁',
|
||||
icon: 'add_moderator',
|
||||
value: '50',
|
||||
color1: '#BB3E03',
|
||||
color2: '#BB3E03'
|
||||
},
|
||||
{
|
||||
title: '进行中的威胁',
|
||||
icon: 'stream',
|
||||
value: '1020',
|
||||
color1: '#AE2012',
|
||||
color2: '#AE2012'
|
||||
}
|
||||
],
|
||||
threatStatistics: {
|
||||
all: 1,
|
||||
confirm: 0,
|
||||
ingore: 1,
|
||||
working: 0,
|
||||
host_list: {},
|
||||
host_num: 10,
|
||||
all_log_num: 647
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateCookie (selectItem) {
|
||||
const b64Obj = new Base64()
|
||||
Cookies.set('custom_threat_item', b64Obj.encode(JSON.stringify(this.Threatitems)))
|
||||
},
|
||||
get_threatStatistics () {
|
||||
axios
|
||||
.get('/api/v1/get/threat_statistics', {
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
.then((response) => {
|
||||
const data = response.data
|
||||
if (data.data) {
|
||||
this.threatStatistics = data.data
|
||||
this.threatStatistics.host_num = Object.keys(this.threatStatistics.host_list).length
|
||||
// Threatitems
|
||||
this.Threatitems[0].value = this.threatStatistics.all
|
||||
this.Threatitems[1].value = this.threatStatistics.confirm
|
||||
this.Threatitems[2].value = this.threatStatistics.ingore
|
||||
this.Threatitems[3].value = this.threatStatistics.working
|
||||
console.log(this.threatStatistics)
|
||||
|
||||
this.draw()
|
||||
}
|
||||
})
|
||||
},
|
||||
draw () {
|
||||
const hostList = []
|
||||
const hostLoggedNumList = []
|
||||
for (const key in this.threatStatistics.host_list) {
|
||||
hostList.push(key)
|
||||
hostLoggedNumList.push({
|
||||
itemStyle: {
|
||||
color: '#005F73'
|
||||
},
|
||||
name: key,
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
areaStyle: {},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: this.threatStatistics.host_list[key].log_num
|
||||
})
|
||||
}
|
||||
const dom = this.$refs.main_draw
|
||||
const myChart = echarts.init(dom)
|
||||
const option = {
|
||||
title: {
|
||||
text: '最近十分钟日志量'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: hostList
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [{
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['10min', '9min', '8min', '7min', '6min', '5min', '4min', '3min', '2min', '1min']
|
||||
}],
|
||||
yAxis: [{
|
||||
type: 'value'
|
||||
}],
|
||||
series: hostLoggedNumList
|
||||
}
|
||||
myChart.setOption(option)
|
||||
setTimeout(() => {
|
||||
myChart.resize()
|
||||
}, 1000)
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.get_threatStatistics()
|
||||
setInterval(() => {
|
||||
this.get_threatStatistics()
|
||||
}, 10000)
|
||||
const cookieCustomThreatItem = Cookies.get('custom_threat_item')
|
||||
if (cookieCustomThreatItem) {
|
||||
const b64Obj = new Base64()
|
||||
|
||||
this.Threatitems = JSON.parse(b64Obj.decode(cookieCustomThreatItem))
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,203 +1,198 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="q-gutter-md q-mb-sm q-pa-lg">
|
||||
<q-card class="bg-transparent no-shadow no-border">
|
||||
<q-card-section class="q-pa-none">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div
|
||||
v-for="(item, index) in Threatitems"
|
||||
:key="index"
|
||||
class="col-md-3 col-sm-12 col-xs-12"
|
||||
>
|
||||
<q-item
|
||||
:style="`background-color: ${item.color1}`"
|
||||
class="q-pa-none"
|
||||
>
|
||||
<q-item-section
|
||||
side
|
||||
:style="`background-color: ${item.color2}`"
|
||||
class="q-pa-lg q-mr-none text-white"
|
||||
>
|
||||
<q-icon :name="item.icon" color="white" size="24px"></q-icon>
|
||||
</q-item-section>
|
||||
<q-item-section class="q-pa-md q-ml-none text-white">
|
||||
<q-item-label class="text-white text-h6 text-weight-bolder">{{
|
||||
item.value
|
||||
}}</q-item-label>
|
||||
<q-item-label>{{ item.title }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col"></div>
|
||||
<div class="col">
|
||||
<div class="row q-gutter-md q-mb-sm q-pa-lg">
|
||||
<q-timeline layout="dense" side="right" color="red">
|
||||
<template
|
||||
v-if="!server_threat.data || server_threat.data.length == 0"
|
||||
>
|
||||
<h4>暂无可用数据,下次刷新时间 {{last_refresh}}...</h4>
|
||||
</template>
|
||||
<template
|
||||
v-for="(threat, index) in server_threat.data"
|
||||
:key="index"
|
||||
>
|
||||
<q-timeline-entry :subtitle="'主机:' + threat.host" side="left">
|
||||
<div>
|
||||
<q-card
|
||||
flat
|
||||
bordered
|
||||
style="overflow: auto"
|
||||
:thumb-style="thumbStyle"
|
||||
:bar-style="barStyle"
|
||||
>
|
||||
<q-card-section horizontal>
|
||||
<div class="bg-red-5"> </div>
|
||||
<q-card-actions vertical class="justify-around q-px-md">
|
||||
<div>进程链hash: {{ threat.chain_hash }}</div>
|
||||
<div>进程: {{ threat.start_process.path }}</div>
|
||||
<div>用户: {{ threat.start_process.user }}</div>
|
||||
<div>
|
||||
分数:
|
||||
<q-chip
|
||||
square
|
||||
color="orange"
|
||||
text-color="white"
|
||||
icon-right="visibility"
|
||||
>
|
||||
{{ threat.risk_score }}
|
||||
</q-chip>
|
||||
</div>
|
||||
<div>
|
||||
活动状态:
|
||||
<q-chip
|
||||
square
|
||||
:color="threat.is_end == 1 ? 'negative' : 'red'"
|
||||
text-color="white"
|
||||
>
|
||||
{{ threat.is_end == 1 ? "已结束" : "进行中" }}
|
||||
</q-chip>
|
||||
</div>
|
||||
<div>
|
||||
产生的威胁:
|
||||
<template
|
||||
v-for="(index, operation) in threat.hit_rule"
|
||||
:key="index"
|
||||
>
|
||||
<q-chip square color="rgb(239,243,246)">
|
||||
{{ operation }} ({{ index }})
|
||||
</q-chip>
|
||||
</template>
|
||||
</div>
|
||||
<div>
|
||||
<q-btn
|
||||
flat
|
||||
color="accent"
|
||||
@click="show_details(threat.id)"
|
||||
icon="open_in_new"
|
||||
>
|
||||
查看详情
|
||||
</q-btn>
|
||||
<q-btn
|
||||
flat
|
||||
color="accent"
|
||||
@click="search_vt(threat.start_process.hash)"
|
||||
icon="search"
|
||||
>
|
||||
在VT上搜索
|
||||
</q-btn>
|
||||
<q-btn
|
||||
flat
|
||||
color="accent"
|
||||
@click="handle_threat(threat.id, 1)"
|
||||
icon="done"
|
||||
>
|
||||
确认威胁
|
||||
</q-btn>
|
||||
<q-btn
|
||||
flat
|
||||
color="accent"
|
||||
@click="handle_threat(threat.id, 2)"
|
||||
icon="texture"
|
||||
>
|
||||
忽略威胁
|
||||
</q-btn>
|
||||
<q-btn
|
||||
flat
|
||||
color="accent"
|
||||
icon="close"
|
||||
@click="delete_threat(threat.id)"
|
||||
>
|
||||
删除报警
|
||||
</q-btn>
|
||||
</div>
|
||||
</q-card-actions>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</q-timeline-entry>
|
||||
</template>
|
||||
</q-timeline>
|
||||
<div class="col"></div>
|
||||
<div class="col">
|
||||
<div class="row q-gutter-md q-mb-sm q-pa-lg">
|
||||
<q-timeline layout="dense" side="right" color="red">
|
||||
<template v-if="!server_threat.data || server_threat.data.length == 0">
|
||||
<h4>暂无可用数据,下次刷新时间 {{last_refresh}}...</h4>
|
||||
</template>
|
||||
<template v-for="(threat, index) in server_threat.data" :key="index">
|
||||
<q-timeline-entry :subtitle="'主机:' + threat.host" side="left">
|
||||
<div>
|
||||
<q-card flat bordered style="overflow: auto" :thumb-style="thumbStyle" :bar-style="barStyle">
|
||||
<q-card-section horizontal>
|
||||
<div class="bg-red-5"> </div>
|
||||
<q-card-actions vertical class="justify-around q-px-md">
|
||||
<div>进程链hash: {{ threat.chain_hash }}</div>
|
||||
<div>进程: {{ threat.start_process.path }}</div>
|
||||
<div>用户: {{ threat.start_process.user }}</div>
|
||||
<div>
|
||||
分数:
|
||||
<q-chip square color="orange" text-color="white" icon-right="visibility">
|
||||
{{ threat.risk_score }}
|
||||
</q-chip>
|
||||
</div>
|
||||
<div>
|
||||
活动状态:
|
||||
<q-chip square :color="threat.is_end == 1 ? 'negative' : 'red'" text-color="white">
|
||||
{{ threat.is_end == 1 ? "已结束" : "进行中" }}
|
||||
</q-chip>
|
||||
</div>
|
||||
<div>
|
||||
ATTCK命中:
|
||||
<template v-for="(index, operation) in threat.attck_hit_list" :key="index">
|
||||
<q-chip square color="rgb(239,243,246)">
|
||||
{{ operation }} ({{ index }})
|
||||
</q-chip>
|
||||
</template>
|
||||
</div>
|
||||
<div>
|
||||
产生的威胁:
|
||||
<template v-for="(index, operation) in threat.hit_rule" :key="index">
|
||||
<q-chip square color="red" text-color="white">
|
||||
{{ operation }} ({{ index }})
|
||||
</q-chip>
|
||||
</template>
|
||||
<template v-if="JSON.stringify(threat.hit_rule) == '{}'">
|
||||
<q-chip square color="negative" text-color="white">
|
||||
<!--crowdstrike: 这活我熟-->
|
||||
机器学习引擎
|
||||
</q-chip>
|
||||
</template>
|
||||
</div>
|
||||
<div>
|
||||
<q-btn flat color="accent" @click="show_details(threat.id)" icon="open_in_new">
|
||||
查看详情
|
||||
</q-btn>
|
||||
<q-btn flat color="accent" @click="search_vt(threat.start_process.hash)" icon="search">
|
||||
在VT上搜索
|
||||
</q-btn>
|
||||
<q-btn flat color="accent" @click="handle_threat(threat.id, 1)" icon="done">
|
||||
确认威胁
|
||||
</q-btn>
|
||||
<q-btn flat color="accent" @click="handle_threat(threat.id, 2)" icon="texture">
|
||||
忽略威胁
|
||||
</q-btn>
|
||||
<q-btn flat color="accent" icon="close" @click="delete_threat(threat.id)">
|
||||
删除报警
|
||||
</q-btn>
|
||||
</div>
|
||||
</q-card-actions>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</q-timeline-entry>
|
||||
</template>
|
||||
</q-timeline>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
</div>
|
||||
<q-dialog
|
||||
v-model="dialog"
|
||||
persistent
|
||||
:maximized="maximizedToggle"
|
||||
transition-show="slide-up"
|
||||
transition-hide="slide-down"
|
||||
>
|
||||
<q-card class="text-white">
|
||||
<q-bar>
|
||||
<q-space></q-space>
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
icon="minimize"
|
||||
@click="maximizedToggle = false"
|
||||
:disable="!maximizedToggle"
|
||||
>
|
||||
<q-tooltip
|
||||
v-if="maximizedToggle"
|
||||
content-class="bg-white text-primary"
|
||||
>Minimize</q-tooltip
|
||||
>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
icon="crop_square"
|
||||
@click="maximizedToggle = true"
|
||||
:disable="maximizedToggle"
|
||||
>
|
||||
<q-tooltip
|
||||
v-if="!maximizedToggle"
|
||||
content-class="bg-white text-primary"
|
||||
>Maximize</q-tooltip
|
||||
>
|
||||
</q-btn>
|
||||
<q-btn dense flat icon="close" v-close-popup>
|
||||
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip>
|
||||
</q-btn>
|
||||
</q-bar>
|
||||
<div class="row" style="width: 100%; height: 100%">
|
||||
<div ref="main_draw" style="width: 100%; height: 100%; margin-left: 5%">
|
||||
1
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<q-dialog v-model="addwhiteListHash" persistent transition-show="scale" transition-hide="scale">
|
||||
<q-card style="min-width: 350px">
|
||||
<q-card-section>
|
||||
<div class="text-h6">填写缘由</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<q-input dense v-model="this.whiteListPostData.reason" autofocus />
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right" class="text-primary">
|
||||
<q-btn flat label="取消" @click="addwhiteListHash = false" v-close-popup />
|
||||
<q-btn flat label="加入白名单" v-close-popup @click="add_to_white_hash_post()" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</q-dialog>
|
||||
<q-dialog v-model="dialog" persistent :maximized="maximizedToggle" transition-show="slide-up" transition-hide="slide-down">
|
||||
<q-card class="text-white">
|
||||
<q-bar>
|
||||
<q-space></q-space>
|
||||
<q-btn dense flat icon="close" v-close-popup>
|
||||
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip>
|
||||
</q-btn>
|
||||
</q-bar>
|
||||
<div class="row" style="width: 100%; height: 100%">
|
||||
<div ref="main_draw" style="width: 100%; height: 100%; margin-left: 5%">
|
||||
1
|
||||
</div>
|
||||
</div>
|
||||
<q-drawer show-if-above v-if="processChainShowDetails" v-model="processChainShowDetails" side="right" bordered width="350" class="text-dark">
|
||||
<q-list style="width: 100%;word-break: break-all;">
|
||||
<q-item>
|
||||
<q-item-section>活跃状态: {{processChainDetails.active ? "运行中" : "已结束"}}</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
<q-item>
|
||||
<q-item-section>进程名字: {{processChainDetails.name}}</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
<q-item>
|
||||
<q-item-section>进程路径: {{processChainDetails.path}}</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
<q-item>
|
||||
<q-item-section>进程参数: {{processChainDetails.params}}</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
<q-item>
|
||||
<q-item-section>进程id: {{processChainDetails.pid}}</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
<q-item>
|
||||
<q-item-section>父进程id: {{processChainDetails.ppid}}</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
<q-item>
|
||||
<q-item-section>进程hash: {{processChainDetails.md5}}</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
<q-item>
|
||||
<q-item-section>是否在白名单中: {{processChainDetails.isWhite ? "是" : "否"}}</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
<q-item>
|
||||
<q-item-section>进程命中的规则:
|
||||
<template v-for="(index, operation) in processChainDetails.hitRules" :key="index">
|
||||
<q-chip square color="rgb(239,243,246)">
|
||||
{{ operation }} ({{ index }})
|
||||
</q-chip>
|
||||
</template>
|
||||
<template v-if="JSON.stringify(processChainDetails.hitRules) == '{}'">
|
||||
<q-chip square color="rgb(239,243,246)">
|
||||
无
|
||||
</q-chip>
|
||||
</template>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>attck矩阵:
|
||||
<template v-for="(index, operation) in processChainDetails.hitAttck" :key="index">
|
||||
<q-chip square color="rgb(239,243,246)">
|
||||
{{ operation }} ({{ index }})
|
||||
</q-chip>
|
||||
</template>
|
||||
<template v-if="JSON.stringify(processChainDetails.hitAttck) == '{}'">
|
||||
<q-chip square color="rgb(239,243,246)">
|
||||
无
|
||||
</q-chip>
|
||||
</template>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-btn icon="search" outline style="color: grey;width: 100%;" label="搜索hash" @click="search_vt(processChainDetails.md5)" />
|
||||
</q-item>
|
||||
<q-item>
|
||||
<template v-if="processChainDetails.isWhite == false">
|
||||
<q-btn icon="texture" outline style="color: grey;width: 100%;" label="加入白名单" @click="add_to_white_hash_pre(processChainDetails.path,processChainDetails.md5)" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<q-btn icon="clear" outline style="color: grey;width: 100%;" label="从白名单中删除" @click="delete_white_hash(processChainDetails.md5)" />
|
||||
</template>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-drawer>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
import {
|
||||
defineComponent
|
||||
} from 'vue'
|
||||
|
||||
import axios from 'axios'
|
||||
import * as echarts from 'echarts'
|
||||
@@ -205,7 +200,22 @@ export default defineComponent({
|
||||
name: 'PageIndex',
|
||||
data: function () {
|
||||
return {
|
||||
addwhiteListHash: false,
|
||||
whiteListPostData: {
|
||||
path: '',
|
||||
hash: '',
|
||||
reason: ''
|
||||
},
|
||||
processChainShowDetails: false,
|
||||
last_refresh: 360,
|
||||
processChainDetails: {
|
||||
hash: '',
|
||||
prams: '',
|
||||
hitRules: [],
|
||||
hitAttck: [],
|
||||
isWhite: false,
|
||||
whiteListReason: ''
|
||||
},
|
||||
thumbStyle: {
|
||||
right: '4px',
|
||||
borderRadius: '5px',
|
||||
@@ -220,43 +230,6 @@ export default defineComponent({
|
||||
width: '9px',
|
||||
opacity: 0.2
|
||||
},
|
||||
threatStatistics: {
|
||||
all: 1,
|
||||
confirm: 0,
|
||||
ingore: 1,
|
||||
working: 0
|
||||
},
|
||||
Threatitems:
|
||||
[
|
||||
{
|
||||
title: '发现的威胁',
|
||||
icon: 'remove_red_eye',
|
||||
value: '200',
|
||||
color1: '#5064b5',
|
||||
color2: '#3e51b5'
|
||||
},
|
||||
{
|
||||
title: '确认的威胁',
|
||||
icon: 'flash_on',
|
||||
value: '500',
|
||||
color1: '#f37169',
|
||||
color2: '#f34636'
|
||||
},
|
||||
{
|
||||
title: '忽略的威胁',
|
||||
icon: 'texture',
|
||||
value: '50',
|
||||
color1: '#ea6a7f',
|
||||
color2: '#ea4b64'
|
||||
},
|
||||
{
|
||||
title: '进行中的威胁',
|
||||
icon: 'bar_chart',
|
||||
value: '1020',
|
||||
color1: '#a270b1',
|
||||
color2: '#9f52b1'
|
||||
}
|
||||
],
|
||||
dialog: false,
|
||||
maximizedToggle: true,
|
||||
server_threat: {},
|
||||
@@ -264,6 +237,32 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
delete_white_hash (hash) {
|
||||
axios.get('/api/v1/del/white_list?hash=' + hash).then(res => {
|
||||
this.processChainDetails.isWhite = false
|
||||
})
|
||||
},
|
||||
query_white_hash (hash) {
|
||||
axios.get('/api/v1/query/white_list?hash=' + hash).then(res => {
|
||||
this.processChainDetails.isWhite = res.data.result === 1
|
||||
})
|
||||
},
|
||||
add_to_white_hash_pre (path, hash) {
|
||||
this.whiteListPostData = {
|
||||
path: path,
|
||||
hash: hash,
|
||||
reason: ''
|
||||
}
|
||||
this.addwhiteListHash = true
|
||||
console.log('addwhiteListHash', this.addwhiteListHash)
|
||||
},
|
||||
add_to_white_hash_post () {
|
||||
axios
|
||||
.post('/api/v1/set/white_list', this.whiteListPostData)
|
||||
.then((response) => {
|
||||
this.processChainDetails.isWhite = true
|
||||
})
|
||||
},
|
||||
set_chain_data (data) {
|
||||
if (data.path) {
|
||||
const str = data.path.split('\\')
|
||||
@@ -285,66 +284,80 @@ export default defineComponent({
|
||||
formatter: function (params) {
|
||||
const contextData = params.data
|
||||
let result =
|
||||
'<div>参数: ' +
|
||||
contextData.params +
|
||||
'</div>' +
|
||||
'<div> hash: ' +
|
||||
contextData.md5 +
|
||||
'</div><div>命名规则列表: '
|
||||
'<div>参数: ' +
|
||||
contextData.params +
|
||||
'</div>' +
|
||||
'<div> hash: ' +
|
||||
contextData.md5 +
|
||||
'</div><div>命名规则列表: '
|
||||
if (contextData.operationlist.length === 0) {
|
||||
result += '无'
|
||||
}
|
||||
for (const key in contextData.operationlist) {
|
||||
result +=
|
||||
' ' + key + '[' + contextData.operationlist[key] + ']' + ' '
|
||||
' ' + key + '[' + contextData.operationlist[key] + ']' + ' '
|
||||
}
|
||||
result += '</div>'
|
||||
return result
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
roam: true,
|
||||
type: 'tree',
|
||||
id: 0,
|
||||
name: 'tree1',
|
||||
data: [this.select_chain_data],
|
||||
top: '5%',
|
||||
left: '15%',
|
||||
bottom: '22%',
|
||||
right: '20%',
|
||||
edgeShape: 'polyline',
|
||||
edgeForkPosition: '63%',
|
||||
initialTreeDepth: 60,
|
||||
lineStyle: {
|
||||
width: 2
|
||||
},
|
||||
series: [{
|
||||
roam: true,
|
||||
type: 'tree',
|
||||
id: 0,
|
||||
name: 'tree1',
|
||||
data: [this.select_chain_data],
|
||||
top: '5%',
|
||||
left: '15%',
|
||||
bottom: '22%',
|
||||
right: '20%',
|
||||
edgeShape: 'polyline',
|
||||
edgeForkPosition: '63%',
|
||||
initialTreeDepth: 60,
|
||||
lineStyle: {
|
||||
width: 2
|
||||
},
|
||||
label: {
|
||||
backgroundColor: '#fff',
|
||||
position: 'left',
|
||||
verticalAlign: 'middle',
|
||||
align: 'right'
|
||||
},
|
||||
leaves: {
|
||||
label: {
|
||||
backgroundColor: '#fff',
|
||||
position: 'left',
|
||||
position: 'right',
|
||||
verticalAlign: 'middle',
|
||||
align: 'right'
|
||||
},
|
||||
leaves: {
|
||||
label: {
|
||||
position: 'right',
|
||||
verticalAlign: 'middle',
|
||||
align: 'left'
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'descendant'
|
||||
},
|
||||
symbolSize: [40, 50], // 宽40 高50
|
||||
symbol:
|
||||
'image://data:image/png;base64,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',
|
||||
expandAndCollapse: true,
|
||||
animationDuration: 550,
|
||||
animationDurationUpdate: 750
|
||||
}
|
||||
]
|
||||
align: 'left'
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'descendant'
|
||||
},
|
||||
symbolSize: [30, 30], // 宽40 高50
|
||||
symbol: 'image://data:image/png;base64,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',
|
||||
expandAndCollapse: false,
|
||||
animationDuration: 350,
|
||||
animationDurationUpdate: 450
|
||||
}]
|
||||
}
|
||||
myChart.setOption(option)
|
||||
myChart.on('click', params => {
|
||||
const data = params.data
|
||||
this.processChainDetails = {
|
||||
path: data.path,
|
||||
active: data.active,
|
||||
md5: data.md5,
|
||||
name: data.name,
|
||||
params: data.params,
|
||||
pid: data.pid,
|
||||
ppid: data.ppid,
|
||||
hitRules: data.operationlist === undefined ? {} : data.operationlist,
|
||||
hitAttck: data.attck_hit_list === undefined ? {} : data.attck_hit_list,
|
||||
isWhite: false
|
||||
}
|
||||
this.query_white_hash(data.md5)
|
||||
this.processChainShowDetails = true
|
||||
})
|
||||
},
|
||||
search_vt (hash) {
|
||||
window.open('https://www.virustotal.com/gui/search/' + hash, '_blank')
|
||||
@@ -384,23 +397,7 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
},
|
||||
get_threatStatistics () {
|
||||
axios
|
||||
.get('/api/v1/get/threat_statistics', {
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
.then((response) => {
|
||||
const data = response.data
|
||||
if (data.data) {
|
||||
this.threatStatistics = data.data
|
||||
// Threatitems
|
||||
this.Threatitems[0].value = this.threatStatistics.all
|
||||
this.Threatitems[1].value = this.threatStatistics.confirm
|
||||
this.Threatitems[2].value = this.threatStatistics.ingore
|
||||
this.Threatitems[3].value = this.threatStatistics.working
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
get_clientids () {
|
||||
const queryType = this.$route.params.queryIndex
|
||||
const queryIndex = (queryType === null || queryType === undefined) ? 0 : queryType
|
||||
@@ -415,7 +412,6 @@ export default defineComponent({
|
||||
data: []
|
||||
}
|
||||
this.server_threat.data = data.data
|
||||
this.get_threatStatistics()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
132
Web/syseye/src/pages/Whitelist.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<q-table class="q-pa-lg" :dense="$q.screen.lt.md" title="白名单列表" :columns="data_columns" :rows="data_columns_data" :loading="loading" v-model:pagination="pagination" @request="onRequest">
|
||||
<template v-slot:body="props">
|
||||
<q-tr :props="props">
|
||||
<q-td key="path" :props="props">{{ props.row.path }}</q-td>
|
||||
<q-td key="hash" :props="props">{{ props.row.hash }}</q-td>
|
||||
<q-td key="reason" :props="props">{{ props.row.reason }}</q-td>
|
||||
<q-td key="timestamp" :props="props">{{ time_parase(props.row.timestamp) }}</q-td>
|
||||
<q-td key="action" :props="props">
|
||||
<q-btn color="red" label="移除白名单" @click="delete_white_hash(props.row.hash)" />
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
</q-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
defineComponent
|
||||
} from 'vue'
|
||||
import axios from 'axios'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'WhiteList',
|
||||
data: function () {
|
||||
return {
|
||||
data_columns: [{
|
||||
name: 'path',
|
||||
align: 'center',
|
||||
label: '路径',
|
||||
field: 'path'
|
||||
},
|
||||
{
|
||||
name: 'hash',
|
||||
align: 'center',
|
||||
label: 'hash',
|
||||
field: 'hash'
|
||||
},
|
||||
{
|
||||
name: 'reason',
|
||||
align: 'center',
|
||||
label: '原因',
|
||||
field: 'reason'
|
||||
},
|
||||
{
|
||||
name: 'timestamp',
|
||||
align: 'center',
|
||||
label: '时间',
|
||||
field: 'timestamp'
|
||||
},
|
||||
{
|
||||
name: 'action',
|
||||
align: 'center',
|
||||
label: '操作',
|
||||
field: 'steamid'
|
||||
}
|
||||
],
|
||||
data_columns_data: [],
|
||||
loading: false,
|
||||
pagination: {
|
||||
sortBy: 'desc',
|
||||
descending: false,
|
||||
page: 1,
|
||||
rowsPerPage: 10,
|
||||
rowsNumber: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.onRequest({
|
||||
pagination: this.pagination,
|
||||
filter: undefined
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
delete_white_hash (hash) {
|
||||
axios.get('/api/v1/del/white_list?hash=' + hash).then(res => {
|
||||
this.onRequest({
|
||||
pagination: this.pagination,
|
||||
filter: undefined
|
||||
})
|
||||
})
|
||||
},
|
||||
time_parase (pTime) {
|
||||
// shijianchuo是整数,否则要parseInt转换
|
||||
const add0 = m => {
|
||||
return m < 10 ? '0' + m : m
|
||||
}
|
||||
const time = new Date(Number(pTime))
|
||||
console.log('time', pTime)
|
||||
const y = time.getFullYear()
|
||||
const m = time.getMonth() + 1
|
||||
const d = time.getDate()
|
||||
const h = time.getHours()
|
||||
const mm = time.getMinutes()
|
||||
const s = time.getSeconds()
|
||||
return (
|
||||
y +
|
||||
'-' +
|
||||
add0(m) +
|
||||
'-' +
|
||||
add0(d) +
|
||||
' ' +
|
||||
add0(h) +
|
||||
':' +
|
||||
add0(mm) +
|
||||
':' +
|
||||
add0(s)
|
||||
)
|
||||
},
|
||||
onRequest (props) {
|
||||
this.data_columns_data = []
|
||||
this.loading = true
|
||||
const {
|
||||
page
|
||||
} = props.pagination
|
||||
axios.get('/api/v1/query/white_list_all').then(response => {
|
||||
const data = response.data.result
|
||||
console.log(data)
|
||||
for (let index = 0; index < data.length; index++) {
|
||||
const element = data[index]
|
||||
this.data_columns_data.push(element)
|
||||
}
|
||||
this.pagination.page = page
|
||||
this.pagination.rowsNumber = this.data_columns_data.length
|
||||
this.pagination.rowsPerPage = this.data_columns_data.length
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -4,7 +4,7 @@ const routes = [
|
||||
path: '/',
|
||||
component: () => import('layouts/MainLayout.vue'),
|
||||
children: [
|
||||
{ path: '', component: () => import('pages/Index.vue') }
|
||||
{ path: '', component: () => import('pages/Dashboard.vue') }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -12,7 +12,8 @@ const routes = [
|
||||
component: () => import('layouts/MainLayout.vue'),
|
||||
children: [
|
||||
{ path: 'dashboard', component: () => import('pages/Dashboard.vue') },
|
||||
{ path: 'index', name: 'index', component: () => import('pages/Index.vue') }
|
||||
{ path: 'index', name: 'index', component: () => import('pages/Index.vue') },
|
||||
{ path: 'index', name: 'whitelist', component: () => import('pages/Whitelist.vue') }
|
||||
]
|
||||
},
|
||||
// Always leave this as last one,
|
||||
|
||||
106
doc_day0_rule.md
Normal file
@@ -0,0 +1,106 @@
|
||||
### 规则编写教程
|
||||
|
||||
首先明确一点,rmeye有绝大部分威胁的检出能力,但是受限于规则,这些能力相当于只记录但是没生效.因此编写规则是一件重要的事情.在一切的开始,请您阅读ATT&CK的思想:
|
||||
|
||||
https://key08.com/index.php/2022/08/09/1505.html
|
||||
|
||||
本文以检测`mimikatz的关键dll加载`为例
|
||||
|
||||
`mimikatz`会加载: `C:\\Windows\\System32\\samlib.dll` 本文以他为例
|
||||
|
||||
### sysmon客户端规则
|
||||
|
||||
在编写服务端规则之前,请确保`sysmon`有相关配置.因为`rmeye`依赖于`sysmon`,得有客户端规则,才有服务端规则:
|
||||
|
||||
打开客户端安装目录下的`sysmon.xml`:
|
||||
|
||||

|
||||
|
||||
我们需要检测`dll`加载,所以请把目光看到`ImageLoad`
|
||||
|
||||
其他的`sysmon`字段请自行谷歌或者百度了解.本项目有个`provider.json`那个是`sysmon`的所有字段.可以参考,打开`provider.json` ,找到`ImageLoad`
|
||||
|
||||

|
||||
|
||||
可以看到`sysmon`记录了非常多有用的字段,对于我们来说,我们只需要`ImageLoaded`
|
||||
|
||||
在`sysmon.xml`找到`Imageload`这个block,
|
||||
|
||||
```c
|
||||
<RuleGroup name="" groupRelation="or">
|
||||
<ImageLoad onmatch="include">
|
||||
<!--NOTE: Using "include" with no rules means nothing in this section will be logged-->
|
||||
</ImageLoad>
|
||||
</RuleGroup>
|
||||
```
|
||||
|
||||
我们给他增加一个,接受加载`C:\\Windows\\System32\\samlib.dll`的日志:
|
||||
|
||||
```c
|
||||
<RuleGroup name="" groupRelation="or">
|
||||
<ImageLoad onmatch="include">
|
||||
<ImageLoaded condition="is">C:\Windows\System32\samlib.dll</ImageLoaded>
|
||||
</ImageLoad>
|
||||
</RuleGroup>
|
||||
```
|
||||
|
||||
|
||||
|
||||
保存,更新`sysmon`的配置文件,在`rmeye`的安装目录或者在`sysmon`的安装目录执行:
|
||||
|
||||
```
|
||||
sysmon -c sysmon.xml
|
||||
```
|
||||
|
||||

|
||||
|
||||
看到这个,说明你成功了,其他的说明规则有错误,检查一下你写的规则.
|
||||
|
||||
|
||||
|
||||
### 服务端规则编写
|
||||
|
||||
客户端有了搜集日志能力后,是时候编写服务端的了:
|
||||
|
||||
打开服务端目录下的`server/rules/action.py`
|
||||
|
||||
在末尾添加:
|
||||
|
||||
```
|
||||
{
|
||||
'rules': [
|
||||
'action == "imageload" and imageloaded == "c:\\windows\\system32\\samlib.dll"',
|
||||
],
|
||||
'score': 50,
|
||||
'name': 'samlib的dll被加载'
|
||||
}
|
||||
```
|
||||
|
||||
其中
|
||||
|
||||
```
|
||||
rules: 规则的列表,可以是多个规则,是and关系
|
||||
score: 是分数,跟config.py里面的报警分数有关系
|
||||
name: 规则名字
|
||||
规则全部小写,==代表正常匹配,如果是=~代表使用python正则
|
||||
```
|
||||
|
||||
保存,重启服务端,如果不出意外您应该看得到最新的刚刚增加的规则:
|
||||
|
||||

|
||||
|
||||
**完整的 `服务端规则指南`**
|
||||
|
||||
[doc_server_rule_manual.md](./doc_server_rule_manual.md)
|
||||
|
||||
### 测试
|
||||
|
||||
找个mimikatz运行一下看看:
|
||||
|
||||

|
||||
|
||||
(为了测试,将分数设置高一点准没错)
|
||||
|
||||
至此,您就具备的检测mimikatz的一些行为的能力,当然这行为是不全的而且容易产生很多误报的,很多时候为了减少误报或者实现更精准的检测,您需要高级检出能力,这将在下一章插件检测中介绍.
|
||||
下一章:
|
||||
https://github.com/RoomaSec/RmEye/blob/main/doc_day1_plugin.md
|
||||
148
doc_day1_plugin.md
Normal file
@@ -0,0 +1,148 @@
|
||||
### 编写插件用于检测需要复杂上下文的威胁
|
||||
|
||||
在本章开始前,请先阅读:
|
||||
|
||||
https://github.com/RoomaSec/RmEye/blob/main/doc_day0_rule.md
|
||||
|
||||
rmeye提供了一个简陋的插件接口,用于检测需要上下文帮助的威胁.本文以检测mimikatz为例,编写一个插件:
|
||||
|
||||
mimikatz一定会加载如下dll:
|
||||
|
||||
```C
|
||||
C:\Windows\System32\advapi32.dll
|
||||
C:\Windows\System32\crypt32.dll
|
||||
C:\Windows\System32\cryptdll.dll
|
||||
C:\Windows\System32\gdi32.dll
|
||||
C:\Windows\System32\imm32.dll
|
||||
C:\Windows\System32\kernel32.dll
|
||||
C:\Windows\System32\KernelBase.dll
|
||||
C:\Windows\System32\msasn1.dll
|
||||
C:\Windows\System32\msvcrt.dll
|
||||
C:\Windows\System32\ntdll.dll
|
||||
C:\Windows\System32\rpcrt4.dll
|
||||
C:\Windows\System32\rsaenh.dll
|
||||
C:\Windows\System32\samlib.dll
|
||||
C:\Windows\System32\sechost.dll
|
||||
C:\Windows\System32\secur32.dll
|
||||
C:\Windows\System32\shell32.dll
|
||||
C:\Windows\System32\shlwapi.dll
|
||||
C:\Windows\System32\sspicli.dll
|
||||
C:\Windows\System32\user32.dll
|
||||
C:\Windows\System32\vaultcli.dll
|
||||
```
|
||||
|
||||
当有这些的DLL在一个程序被加载的时候,我们就要注意了.但是我们之前的规则是单条的,没有上下文,因此需要通过插件系统实现,本文默认你已经给sysmon增加了以上的datasoruce
|
||||
|
||||
### 插件编写
|
||||
|
||||
在服务端`plugins`目录下新建文件夹`mimikazt_detect`然后新建一个文件`mimikatz_detect.py`,如下是模板:
|
||||
|
||||
```python
|
||||
import global_vars
|
||||
import process
|
||||
|
||||
rm_plugs_config = {
|
||||
"enable": True, #是否启用插件
|
||||
"author": "huoji",
|
||||
"description": "检测mimikatz",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
||||
#新进程启动
|
||||
def rule_new_process_create(current_process: process.Process, host, raw_log_data, json_log_data):
|
||||
return global_vars.THREAT_TYPE_NONE
|
||||
|
||||
#进程动作
|
||||
def rule_new_process_action(current_process: process.Process, host, raw_log_data, json_log_data):
|
||||
return global_vars.THREAT_TYPE_NONE
|
||||
|
||||
#规则初始化
|
||||
def rule_init():
|
||||
pass
|
||||
|
||||
#插件初始化
|
||||
def plugin_init():
|
||||
print('mimikatz检测插件 2022/9/5 by huoji')
|
||||
|
||||
```
|
||||
|
||||
为了检测,我们需要记录每一个dll加载的行为并且保存到进程上下文中,具体看代码
|
||||
|
||||
```python
|
||||
import global_vars
|
||||
import process
|
||||
|
||||
rm_plugs_config = {
|
||||
"enable": True,
|
||||
"author": "huoji",
|
||||
"description": "检测mimikatz",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
||||
mimikatz_dll_list = [
|
||||
'c:\\windows\\system32\\advapi32.dll',
|
||||
'c:\\windows\\system32\\crypt32.dll',
|
||||
'c:\\windows\\system32\\cryptdll.dll',
|
||||
'c:\\windows\\system32\\gdi32.dll',
|
||||
'c:\\windows\\system32\\imm32.dll',
|
||||
'c:\\windows\\system32\\kernel32.dll',
|
||||
'c:\\windows\\system32\\kernelbase.dll',
|
||||
'c:\\windows\\system32\\msasn1.dll',
|
||||
'c:\\windows\\system32\\msvcrt.dll',
|
||||
'c:\\windows\\system32\\ntdll.dll',
|
||||
'c:\\windows\\system32\\rpcrt4.dll',
|
||||
'c:\\windows\\system32\\rsaenh.dll',
|
||||
'c:\\windows\\system32\\samlib.dll',
|
||||
'c:\\windows\\system32\\sechost.dll',
|
||||
'c:\\windows\\system32\\secur32.dll',
|
||||
'c:\\windows\\system32\\shell32.dll',
|
||||
'c:\\windows\\system32\\shlwapi.dll',
|
||||
'c:\\windows\\system32\\sspicli.dll',
|
||||
'c:\\windows\\system32\\user32.dll',
|
||||
'c:\\windows\\system32\\vaultcli.dll',
|
||||
]
|
||||
|
||||
|
||||
def rule_new_process_create(current_process: process.Process, host, raw_log_data, json_log_data):
|
||||
# 服务端提供了一个 plugin_var 变量用于存放当前进程插件的上下文
|
||||
current_process.plugin_var['mimikatz_matched_num'] = 0
|
||||
current_process.plugin_var['mimikatz_detected'] = False
|
||||
return global_vars.THREAT_TYPE_NONE
|
||||
|
||||
|
||||
def rule_new_process_action(current_process: process.Process, host, raw_log_data, json_log_data):
|
||||
global mimikatz_dll_list
|
||||
# 如果日志的action是imageload(dll加载)
|
||||
if json_log_data['action'] == 'imageload' and current_process.plugin_var['mimikatz_detected'] == False:
|
||||
# 把日志中的dll路径取出来
|
||||
dll_path = json_log_data['data']['imageloaded']
|
||||
|
||||
# 如果dll的路径在mimikatz的路径里面,进程上下文+1
|
||||
if dll_path in mimikatz_dll_list:
|
||||
current_process.plugin_var['mimikatz_matched_num'] += 1
|
||||
if current_process.plugin_var['mimikatz_matched_num'] >= len(mimikatz_dll_list):
|
||||
current_process.set_score(300, "[mimikatz]检测到疑似mimikatz进程")
|
||||
current_process.plugin_var['mimikatz_detected'] = True
|
||||
return global_vars.THREAT_TYPE_PROCESS
|
||||
return global_vars.THREAT_TYPE_NONE
|
||||
|
||||
|
||||
def rule_init():
|
||||
pass
|
||||
|
||||
|
||||
def plugin_init():
|
||||
print('mimikatz检测插件 2022/9/5 by huoji')
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 测试
|
||||
|
||||
运行mimikatz:
|
||||

|
||||
|
||||
当然还会有其他的情况的误报!这需要你完善插件.
|
||||
|
||||
如果遇到不懂的地方,可以提issue.欢迎提问
|
||||
156
doc_server_rule_manual.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# 服务端规则指南
|
||||
|
||||
编写服务端规则前,请您详细阅读本文,以便了解规则背后的故事,并帮助您更好的编写 RmEye 规则。
|
||||
|
||||
|
||||
# 规则在何时被应用
|
||||
|
||||
RmEye 通过本地部署的客户端,向服务器传输行为事件。 \
|
||||
服务器在收集并解析了行为事件日志后,会立即调用规则匹配函数检测该行为是否被某条规则命中。
|
||||
|
||||
|
||||
# 规则分类
|
||||
|
||||
基于 RmEye 的设计思想,一切动作都基于其进程载体,所以,规则被分为 动作(action)和 进程(process)两种类型。
|
||||
|
||||
- [`进程规则`]
|
||||
|
||||
用于在进程启动事件日志中,检测新进程的启动上下文,判断其是否为可疑行为。
|
||||
|
||||
- [`动作规则`]
|
||||
|
||||
用于在非进程启动事件日志中,检测其特定行为上下文,判断其是否为可疑行为。
|
||||
|
||||
RmEye 动作规则列表编写于 `/Server/rules/py/action.py` 文件中;进程规则列表编写于 `/Server/rules/py/process.py` 文件中。
|
||||
|
||||
|
||||
# 规则单元数据结构
|
||||
|
||||
```json
|
||||
{
|
||||
'rules': [
|
||||
'originalfilename =~ ".*wbadmin.exe.*" and commandline =~ ".*delete.*"',
|
||||
],
|
||||
'score': 70,
|
||||
'name': '通过wbadmin删除备份'
|
||||
}
|
||||
```
|
||||
这是一个 进程(process)类型的示例规则单元,它是一个 dict 数据,包含有三个 item,\
|
||||
分别是:`rules`, `score`, `name`
|
||||
|
||||
- [`rules`]-> `list`:
|
||||
|
||||
其中包含一个或多个使用 `rule_engine` 语法的规则匹配表达式,每个表达式间的关系为 `或`,即任何一个表达式被匹配,都认为该规则已命中。
|
||||
|
||||
- [`score`]-> `int`:
|
||||
|
||||
由一个整数表示的规则匹配分值
|
||||
|
||||
- [`name`]-> `str`:
|
||||
|
||||
规则名称
|
||||
|
||||
# 适用于 RmEye 的 `rule_engine` 规则匹配表达式
|
||||
|
||||
`rule_engine` 表达式是服务端规则的核心,它允许用户定义一个 key-value 类型的 Query 表达式,以匹配一个 dict 数据;\
|
||||
表达式的左值匹配 dict 数据中的特定键名(key),\
|
||||
右值允许适用通配符、数字、字符串等进行完全匹配或模糊匹配 dict 数据中,对应左值键名的值(value)。\
|
||||
需要特别注意的是,必须定义 RmEye 数据源事件日志中存在的左值,才可以使规则完全按照预期工作。
|
||||
|
||||
# 进程规则已支持的通用左值定义
|
||||
|
||||
- `processid`
|
||||
|
||||
进程 PID
|
||||
|
||||
- `image`
|
||||
|
||||
进程文件路径
|
||||
|
||||
- `originalfilename`
|
||||
|
||||
进程原始文件名
|
||||
|
||||
- `hashes`
|
||||
|
||||
进程 MD5 哈希
|
||||
|
||||
- `commandline`
|
||||
|
||||
进程命令行
|
||||
|
||||
- `user`
|
||||
|
||||
进程用户名
|
||||
|
||||
- `integritylevel`
|
||||
|
||||
进程权限等级
|
||||
|
||||
- `parentprocessid`
|
||||
|
||||
父进程 PID
|
||||
|
||||
- `parentimage`
|
||||
|
||||
父进程文件路径
|
||||
|
||||
- `parentcommandline`
|
||||
|
||||
父进程命令行
|
||||
|
||||
- `parentuser`
|
||||
|
||||
父进程用户
|
||||
|
||||
# 动作规则已支持的特有左值定义
|
||||
|
||||
- `action`
|
||||
|
||||
动作类型,包括:
|
||||
| action | 描述 |
|
||||
| ---- | ---- |
|
||||
| processaccess | 进程句柄访问 |
|
||||
| pipecreate | 命名管道创建 |
|
||||
| createremotethread | 远程线程创建 |
|
||||
| filecreatestreamhash | 文件流创建 |
|
||||
| registryadd | 注册表项新建 |
|
||||
| registryvalueSet | 注册表值项设置 |
|
||||
| registryobjectSet | 注册表对象设置 |
|
||||
| dnsquery | DNS 查询 |
|
||||
| networkconnect | 网络连接建立 |
|
||||
| clipboardchange | 剪贴板访问 |
|
||||
| processtampering | 进程执行流劫持 |
|
||||
| filedeletedetected | 可执行文件删除 |
|
||||
| filecreate | 文件创建 |
|
||||
| imageload | DLL 加载 |
|
||||
| processcreate | 进程创建(已分离为进程规则)|
|
||||
| processterminal | 进程退出(内部保留)|
|
||||
|
||||
- `sourceimage` - 仅适用于动作 `processaccess`
|
||||
|
||||
源进程文件路径
|
||||
|
||||
- `targetimage` - 仅适用于动作 `processaccess`
|
||||
|
||||
目标进程文件路径
|
||||
|
||||
- `grantedaccess` - 仅适用于动作 `processaccess`
|
||||
|
||||
访问权限
|
||||
|
||||
- `calltrace` - 仅适用于动作 `processaccess`
|
||||
|
||||
调用栈(Call Stack)
|
||||
|
||||
- `pipename` - 仅适用于动作 `pipecreate`
|
||||
|
||||
管道名称
|
||||
|
||||
- `targetfilename` - 仅适用于动作 `filecreate`
|
||||
|
||||
目标文件名
|
||||
|
||||
- `imageloaded` - 仅适用于动作 `imageload`
|
||||
|
||||
已加载的映像名
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# RmEye
|
||||
RmEye是一个window上的基于att&ck现代EDR设计思想的威胁响应工具.
|
||||
@@ -19,12 +19,57 @@ RmEye是一个window上的基于att&ck现代EDR设计思想的威胁响应工具
|
||||
5. 对RPC、COM、ALPC基本无能为力
|
||||
6. 不支持更高级的扩展检测,如检测脚本、下发规则,主机链
|
||||
7. 受限于Sysmon,很多att&ck的T没有覆盖,也无法覆盖.
|
||||
8. 没有响应能力,只能被动记录.
|
||||
请牢记,RmEye自身定位是轻量级威胁检出工具
|
||||
|
||||
### 最新新闻
|
||||
2022/10/11:
|
||||
重新设计了一下界面...
|
||||
|
||||
2022/9/29:
|
||||
国庆节更新,增加ip与hash的ioc插件,目前Rmeye有能力对ip和hash进行标注,使用时务必换成自己的apikey,其他请看下面的ioc部分
|
||||
|
||||
2022/9/22:
|
||||
增加仪表盘,可视化展示检测结果
|
||||
|
||||
2022/9/21:
|
||||
修复了秋季更新的几个bug,增加了`networkconnect`和`FileCreateTimeChange`的ds,增加了`brc4`的检测
|
||||
|
||||
2022/9/20:
|
||||
秋季重大更新,规则部分完全重构,目前检出完全基于attck的software.文档有空了再更新
|
||||
|
||||
2022/9/8:
|
||||
增加服务端规则指南: \
|
||||
[doc_server_rule_manual.md](./doc_server_rule_manual.md)
|
||||
|
||||
2022/9/5:
|
||||
增加规则编写教程:
|
||||
https://github.com/RoomaSec/RmEye/blob/main/doc_day0_rule.md
|
||||
增加`mimikatz`检测
|
||||
|
||||
2022/8/31:
|
||||
增加进程白名单系统,现在能给进程加白名单了.在打开进程链后,点击某个进程加入白名单即可
|
||||
|
||||
2022/8/29:
|
||||
增加uac提权检测插件`uac_bypass_detect`,但是受限于sysmon,没有办法获取RPC信息,因此只能检测一部分的UAC提权行为.并且有误报,请酌情考虑
|
||||
|
||||
### 检出截图
|
||||
威胁列表:
|
||||
|
||||
新dashboard(2022/10/11更新):
|
||||

|
||||
新界面(2022/10/11更新):
|
||||

|
||||
|
||||
IOC(2022/10/1更新):
|
||||

|
||||

|
||||
威胁列表(2022/9/20更新):
|
||||

|
||||
powershell:
|
||||
仪表盘(2022/9/22更新):
|
||||

|
||||
进程链行为回溯
|
||||

|
||||
powershell恶意执行:
|
||||

|
||||
apt样本:
|
||||

|
||||
@@ -34,7 +79,12 @@ apt样本:
|
||||

|
||||
offic宏钓鱼:
|
||||

|
||||
|
||||
uac提权检测:
|
||||

|
||||
mimikatz检测:
|
||||

|
||||
brc4检测:
|
||||

|
||||
### 待做列表
|
||||
1. 更好的前端(目前是VUE-CDN模式,不太好,想换成VUE-CLI) 已经完成
|
||||
2. 日志回放【目前重点】
|
||||
@@ -47,7 +97,7 @@ offic宏钓鱼:
|
||||
9. 完善目前的插件系统【目前重点】
|
||||
10. 云日志检测能力【目前重点】
|
||||
### 安装
|
||||
下载release( xxxxxxxx ),里面有客户端,服务端自行clone本项目
|
||||
下载release( https://github.com/RoomaSec/RmEye/releases ),里面有客户端,服务端自行clone本项目
|
||||
服务端是python3编写,安装完依赖库后输入
|
||||
```
|
||||
python webserver.py
|
||||
@@ -103,12 +153,15 @@ sysmon /uninstall
|
||||
```
|
||||
即可干净卫生的卸载掉RmEye
|
||||
|
||||
### IOC
|
||||
目前RmEye使用的是`https://metadefender.opswat.com/`的免费IOC,目前的apikey仅用于测试,自己部署的时候请务必打开`plugins/ioc_opswat/opswat.py`把`"apikey": "010d4868aef799750e2828fdf17a4d98"`换成你自己的,否做会不安全(比如其他人能查得到你的请求记录)/有使用量限制(100次一天).所以务必换成你自己注册的账号.这个IOC源是免费的而且好用的,比OTX好用
|
||||
|
||||
### 规则相关的问题
|
||||
1. 规则目前仅120条,很多攻击面没有覆盖,其他规则请访问《社区》
|
||||
2. 规则目前只支持rule_engine与yara的规则,其中yara的规则支持是以插件的形式支持
|
||||
3. 目前的规则字段完全依赖sysmon的字段,sysmon的字段请检查根目录下的provider.json(但是请记住纯小写,自行做大小写转换)
|
||||
|
||||
规则目前有两种规则:
|
||||
规则目前在`Server/rules`目录规则目前有两种规则:
|
||||
rule_engine:
|
||||
如检测由CMD启动的ipconfig:
|
||||
```
|
||||
@@ -122,8 +175,11 @@ rule_engine:
|
||||
```
|
||||
分数代表的是本次规则给进程链所增加的分数,报警是根据前面的MAX_THREAT_SCORE设置的
|
||||
|
||||
具体编写方法请移步:
|
||||
https://github.com/zeroSteiner/rule-engine
|
||||
规则编写教程请移步:
|
||||
https://github.com/RoomaSec/RmEye/blob/main/doc_day0_rule.md
|
||||
|
||||
规则引擎的语法请移步:
|
||||
https://github.com/zeroSteiner/rule-engine
|
||||
|
||||
yara,需要安装插件,具体请看交流部分
|
||||
|
||||
@@ -138,12 +194,9 @@ https://github.com/VirusTotal/yara
|
||||
https://github.com/SwiftOnSecurity/sysmon-config
|
||||
请遵守相关库的开源协议.相关法律风险本项目不负任何责任
|
||||
|
||||
### 交流
|
||||
开源的目的不是为了免费填鸭式教学,或者被免费拿去发公众号引流、去拿去集成产品方案去赚钱,而是要一起完善这个工具,从而实现共赢.
|
||||
扫一扫加入这个工具内部测试群,这样就能获取实时动态
|
||||

|
||||
|
||||
### 特别感谢
|
||||
@Pwn0x01 yara插件
|
||||
@zeroSteiner 规则引擎插件
|
||||
@SwiftOnSecurity 客户端规则
|
||||
@SwiftOnSecurity 客户端规则
|
||||
@Fplyth0ner-Combie 规则相关文档
|
||||
|
||||
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Flask==2.2.2
|
||||
requests==2.28.1
|
||||
rule_engine==3.5.0
|
||||
SQLAlchemy==1.4.42
|
||||
40
sysmon.xml
@@ -282,7 +282,7 @@
|
||||
<Image name="Usermode" condition="begin with">C:\Users</Image> <!--Tools downloaded by users can use other processes for networking, but this is a very valuable indicator.-->
|
||||
<Image name="Caution" condition="begin with">C:\Recycle</Image> <!--Nothing should operate from the RecycleBin locations.-->
|
||||
<Image condition="begin with">C:\ProgramData</Image> <!--Normally, network communications should be sourced from "Program Files" not from ProgramData, something to look at-->
|
||||
<Image condition="begin with">C:\Windows\Temp</Image> <!--Suspicious anything would communicate from the system-level temp directory-->
|
||||
<Image condition="begin with">C:\Windows\</Image> <!--Suspicious anything would communicate from the system-level temp directory-->
|
||||
<Image name="Caution" condition="begin with">\</Image> <!--Devices and VSC shouldn't be executing changes | Credit: @SBousseaden @ionstorm @neu5ron @PerchedSystems [ https://twitter.com/SwiftOnSecurity/status/1133167323991486464 ] -->
|
||||
<Image name="Caution" condition="begin with">C:\perflogs</Image> <!-- Credit @blu3_team [ https://blu3-team.blogspot.com/2019/05/netconn-from-suspicious-directories.html ] -->
|
||||
<Image name="Caution" condition="begin with">C:\intel</Image> <!-- Credit @blu3_team [ https://blu3-team.blogspot.com/2019/05/netconn-from-suspicious-directories.html ] -->
|
||||
@@ -353,6 +353,8 @@
|
||||
<Image condition="image">nmap.exe</Image>
|
||||
<Image condition="image">psinfo.exe</Image>
|
||||
<!--Ports: Suspicious-->
|
||||
<DestinationPort name="HTTP" condition="is">80</DestinationPort> <!--SSH protocol, monitor admin connections-->
|
||||
<DestinationPort name="HTTPS" condition="is">443</DestinationPort> <!--SSH protocol, monitor admin connections-->
|
||||
<DestinationPort name="SSH" condition="is">22</DestinationPort> <!--SSH protocol, monitor admin connections-->
|
||||
<DestinationPort name="Telnet" condition="is">23</DestinationPort> <!--Telnet protocol, monitor admin connections, insecure-->
|
||||
<DestinationPort name="SMTP" condition="is">25</DestinationPort> <!--SMTP mail protocol port, insecure, used by threats-->
|
||||
@@ -374,8 +376,15 @@
|
||||
|
||||
<RuleGroup name="" groupRelation="or">
|
||||
<NetworkConnect onmatch="exclude">
|
||||
<Image condition="end with">clash-win64.exe</Image>
|
||||
<Image condition="end with">dasHost.exe</Image>
|
||||
<Image condition="end with">DingTalk.exe</Image>
|
||||
<Image condition="end with">vmnat.exe</Image>
|
||||
<Image condition="end with">SysEye.exe</Image>
|
||||
|
||||
<!--SECTION: Microsoft-->
|
||||
<Image condition="begin with">C:\ProgramData\Microsoft\Windows Defender\Platform\</Image>
|
||||
<Image condition="is">C:\Windows\system32\svchost.exe</Image> <!--Microsoft: svchost-->
|
||||
<Image condition="end with">AppData\Local\Microsoft\Teams\current\Teams.exe</Image> <!--Microsoft: Teams-->
|
||||
<DestinationHostname condition="end with">.microsoft.com</DestinationHostname> <!--Microsoft:Update delivery-->
|
||||
<DestinationHostname condition="end with">microsoft.com.akadns.net</DestinationHostname> <!--Microsoft:Update delivery-->
|
||||
@@ -421,7 +430,34 @@
|
||||
<!--DATA: UtcTime, ProcessGuid, ProcessId, Image, ImageLoaded, Hashes, Signed, Signature, SignatureStatus-->
|
||||
<RuleGroup name="" groupRelation="or">
|
||||
<ImageLoad onmatch="include">
|
||||
<!--NOTE: Using "include" with no rules means nothing in this section will be logged-->
|
||||
<ImageLoaded condition="contains">samlib.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">advapi32.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">crypt32.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">cryptdll.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">gdi32.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">imm32.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">msasn1.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">msvcrt.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">rpcrt4.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">rsaenh.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">samlib.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">sechost.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">secur32.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">shell32.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">shlwapi.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">sspicli.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">user32.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">vaultcli.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">dbghelp.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">winhttp.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">credui.dll</ImageLoaded>
|
||||
|
||||
<ImageLoaded condition="contains">dnsapi.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">rtutils.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">urlmon.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">sensapi.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">rasapi32.dll</ImageLoaded>
|
||||
<ImageLoaded condition="contains">napinsp.dll</ImageLoaded>
|
||||
</ImageLoad>
|
||||
</RuleGroup>
|
||||
|
||||
|
||||