init
155
.gitignore
vendored
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
||||||
|
*.db
|
||||||
|
*.zip
|
||||||
BIN
Image/1.png
Normal file
|
After Width: | Height: | Size: 320 KiB |
BIN
Image/2.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
Image/3.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
Image/4.png
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
Image/5.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
Image/6.png
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
Image/logo.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
214
LICENSE
@@ -1,21 +1,201 @@
|
|||||||
MIT License
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
Copyright (c) 2022 RoomaSec
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
1. Definitions.
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
copies or substantial portions of the Software.
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
the copyright owner that is granting the License.
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
other entities that control, are controlled by, or are under common
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
control with that entity. For the purposes of this definition,
|
||||||
SOFTWARE.
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|||||||
151
README.md
@@ -1,2 +1,149 @@
|
|||||||
# RmEye
|

|
||||||
戎马之眼是一个window上的基于att&ck模型的威胁监控工具.有效检测常见的未知威胁与已知威胁.防守方的利剑
|
|
||||||
|
# DuckSysEye
|
||||||
|
SysEye是一个window上的基于att&ck现代EDR设计思想的威胁响应工具.
|
||||||
|
不同于EDR,它轻量、高效.自身定位是轻量级威胁检出工具.
|
||||||
|
而不是繁重的、需要付费的、效果不明的所谓的EDR
|
||||||
|
|
||||||
|
### 功能特点
|
||||||
|
1. 基于att&ck设计.所有设计只是为了符合att&ck的攻击路径、攻击链(虽然规则里面没有标注T因为懒惰)
|
||||||
|
2. 轻量、高效.为了不适用繁重超占内存的ELK设计思路,而且要保证检出的同时保证不会太重,agent端使用了大量规则过滤,这样才使得后端使用sqlite作为数据库成为可能.单机日志平均一天4M.此外轻量级别的客户端一天只占40-400KB的内存.
|
||||||
|
3. 行为检出,让免杀成为过去式.基于att&ck设计,只看行为不看文件.文件类免杀已经成为过去式.
|
||||||
|
4. 高扩展性.可随需求定制功能
|
||||||
|
|
||||||
|
### SysEye 之所以不是 Edr/Xdr/Mdr/Ndr/XXXXXdr
|
||||||
|
1. SysEye没有流量监控
|
||||||
|
2. SysEye仅覆盖20%左右的datasource
|
||||||
|
3. SysEye没有联动WAF、IPS/IDS
|
||||||
|
4. SysEye没有实时拦截功能
|
||||||
|
5. 对RPC、COM、ALPC基本无能为力
|
||||||
|
6. 不支持更高级的扩展检测,如检测脚本、下发规则,主机链
|
||||||
|
7. 受限于Sysmon,很多att&ck的T没有覆盖,也无法覆盖.
|
||||||
|
请牢记,SysEye自身定位是轻量级威胁检出工具
|
||||||
|
|
||||||
|
### 检出截图
|
||||||
|
威胁列表:
|
||||||
|

|
||||||
|
powershell:
|
||||||
|

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

|
||||||
|
勒索软件:
|
||||||
|

|
||||||
|
网站入侵提权到执行cobalt strike:
|
||||||
|

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

|
||||||
|
|
||||||
|
### 待做列表
|
||||||
|
1. 更好的前端(目前是VUE-CDN模式,不太好,想换成VUE-CLI) 已经完成
|
||||||
|
2. 日志回放【目前重点】
|
||||||
|
3. 威胁狩猎【目前重点】
|
||||||
|
4. att&ck热力图
|
||||||
|
5. 在线规则编辑器
|
||||||
|
6. 内网横向检测
|
||||||
|
7. iis、apache、nginx日志搜集分析(aka: XDR的实现)
|
||||||
|
8. 集成反病毒引擎
|
||||||
|
9. 完善目前的插件系统【目前重点】
|
||||||
|
10. 云日志检测能力【目前重点】
|
||||||
|
### 安装
|
||||||
|
下载release( xxxxxxxx ),里面有客户端,服务端自行clone本项目
|
||||||
|
服务端是python3编写,安装完依赖库后输入
|
||||||
|
```
|
||||||
|
python webserver.py
|
||||||
|
```
|
||||||
|
即可部署
|
||||||
|
服务端部署后,修改config.py里面的
|
||||||
|
```
|
||||||
|
# 检出阈值,越高越难检出但是也会越准确
|
||||||
|
MAX_THREAT_SCORE = 170
|
||||||
|
|
||||||
|
# 授权访问主站的IP列表.如果不在后台里面则不能访问后台
|
||||||
|
ALLOW_ACCESS_IP = ['127.0.0.1']
|
||||||
|
```
|
||||||
|
MAX_THREAT_SCORE代表报警分数,意思为进程链总分超过此分数则报警,越高越准但是也会漏报
|
||||||
|
ALLOW_ACCESS_IP代表允许的IP,只有在此名单里面的IP才能访问后台.请增加自己的IP地址
|
||||||
|
|
||||||
|
客户端则编辑config.ini
|
||||||
|
```
|
||||||
|
[communication]
|
||||||
|
server = http://192.168.111.189:5000
|
||||||
|
```
|
||||||
|
其中server改成你的服务端的地址
|
||||||
|
然后分发三个文件给客户端并且放在同一目录:
|
||||||
|
config.ini、install.cmd、SysEye.exe、sysmon.xml、Sysmon64.exe
|
||||||
|
之后管理员身份运行install.cmd安装sysmon与syseye
|
||||||
|
访问 http://服务器ip:5000(flask默认端口) 查看后台
|
||||||
|
当然一开始啥数据也没有,为了确认是否安装成功可以将webserver.py中的
|
||||||
|
```
|
||||||
|
flask_log = logging.getLogger('werkzeug')
|
||||||
|
flask_log.setLevel(logging.ERROR)
|
||||||
|
```
|
||||||
|
注释掉,检查有没有客户端的请求即可
|
||||||
|
手动安装(cmd脚本其实执行了这些命令):
|
||||||
|
```
|
||||||
|
//安装sysmon:
|
||||||
|
sysmon -i
|
||||||
|
//sysmon加载配置项
|
||||||
|
sysmon -c sysmon.xml
|
||||||
|
//安装syseye
|
||||||
|
syseye /install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 卸载
|
||||||
|
卸载syseye:
|
||||||
|
在syseye目录下执行
|
||||||
|
```
|
||||||
|
SysEye /uninstall
|
||||||
|
```
|
||||||
|
如果您需要卸载sysmon
|
||||||
|
执行
|
||||||
|
```
|
||||||
|
sysmon /uninstall
|
||||||
|
```
|
||||||
|
即可干净卫生的卸载掉Syseye
|
||||||
|
|
||||||
|
### 规则相关的问题
|
||||||
|
1. 规则目前仅120条,很多攻击面没有覆盖,其他规则请访问《社区》
|
||||||
|
2. 规则目前只支持rule_engine与yara的规则,其中yara的规则支持是以插件的形式支持
|
||||||
|
3. 目前的规则字段完全依赖sysmon的字段,sysmon的字段请检查根目录下的provider.json(但是请记住纯小写,自行做大小写转换)
|
||||||
|
|
||||||
|
规则目前有两种规则:
|
||||||
|
rule_engine:
|
||||||
|
如检测由CMD启动的ipconfig:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'originalfilename =~ ".*cmd.exe" and commandline =~ ".*ipconfig.*"',
|
||||||
|
],
|
||||||
|
'score': 80,
|
||||||
|
'name': 'cmd启动ipconfig'
|
||||||
|
},
|
||||||
|
```
|
||||||
|
分数代表的是本次规则给进程链所增加的分数,报警是根据前面的MAX_THREAT_SCORE设置的
|
||||||
|
|
||||||
|
具体编写方法请移步:
|
||||||
|
https://github.com/zeroSteiner/rule-engine
|
||||||
|
|
||||||
|
yara,需要安装插件,具体请看交流部分
|
||||||
|
|
||||||
|
### 第三方引用库
|
||||||
|
1. sysmon
|
||||||
|
https://docs.microsoft.com/zh-cn/sysinternals/downloads/sysmon
|
||||||
|
2. rule_engine
|
||||||
|
https://github.com/zeroSteiner/rule-engine
|
||||||
|
3. yara
|
||||||
|
https://github.com/VirusTotal/yara
|
||||||
|
4. sysmon-config(客户端使用的默认的规则,但是我做了一些修改)
|
||||||
|
https://github.com/SwiftOnSecurity/sysmon-config
|
||||||
|
请遵守相关库的开源协议.相关法律风险本项目不负任何责任
|
||||||
|
|
||||||
|
### 交流
|
||||||
|
开源的目的不是为了免费填鸭式教学,或者被免费拿去发公众号引流、去拿去集成产品方案去赚钱,而是要一起完善这个工具,从而实现共赢.
|
||||||
|
目前我们有一个社区,供大家交流.
|
||||||
|
社区地址:http://xxxxxxxxxx
|
||||||
|
|
||||||
|
### 特别感谢
|
||||||
|
@Pwn0x01 yara插件
|
||||||
|
@zeroSteiner 规则引擎插件
|
||||||
|
@SwiftOnSecurity 客户端规则
|
||||||
|
|||||||
4
Server/config.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# 检出阈值,越高越难检出但是也会越准确
|
||||||
|
MAX_THREAT_SCORE = 170
|
||||||
|
# 授权访问主站的IP列表.如果不在后台里面则不能访问后台
|
||||||
|
ALLOW_ACCESS_IP = ['127.0.0.1']
|
||||||
11
Server/global_vars.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
THREAT_TYPE_NONE = -1
|
||||||
|
THREAT_TYPE_PROCESS = 0
|
||||||
|
THREAT_TYPE_ROOTKIT = 1
|
||||||
|
THREAT_TYPE_LM = 2
|
||||||
|
THREAT_TYPE_HOSTSTATUS = 3
|
||||||
|
THREAT_TYPE_NETWORK = 4
|
||||||
|
PLUGS_PATH = os.path.dirname(os.path.realpath(__file__)) + "\\plugins\\"
|
||||||
|
g_plugs = []
|
||||||
195
Server/log.py
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
import process
|
||||||
|
import rule
|
||||||
|
import sql
|
||||||
|
import global_vars
|
||||||
|
import config
|
||||||
|
import plugin
|
||||||
|
|
||||||
|
|
||||||
|
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 = ""
|
||||||
|
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
|
||||||
|
if current_process is not None:
|
||||||
|
if current_process.chain.risk_score >= config.MAX_THREAT_SCORE:
|
||||||
|
if had_threat == global_vars.THREAT_TYPE_PROCESS:
|
||||||
|
current_process.chain.update_process_tree()
|
||||||
|
threat = sql.select_threat_by_chain_id(
|
||||||
|
host, current_process.chain.hash, global_vars.THREAT_TYPE_PROCESS
|
||||||
|
)
|
||||||
|
if len(threat) == 0:
|
||||||
|
process_info: process.Process = None
|
||||||
|
|
||||||
|
if len(current_process.chain.process_list) > 1:
|
||||||
|
process_info = current_process.chain.process_list[1]
|
||||||
|
else:
|
||||||
|
process_info = current_process
|
||||||
|
info_save_data = {
|
||||||
|
"path": process_info.path,
|
||||||
|
"hash": process_info.md5,
|
||||||
|
"params": process_info.params,
|
||||||
|
"user": process_info.user,
|
||||||
|
"create_time": process_info.time,
|
||||||
|
}
|
||||||
|
sql.push_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,
|
||||||
|
json.dumps(info_save_data),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
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,
|
||||||
|
current_process.chain.active == False,
|
||||||
|
)
|
||||||
|
parent_pid = 0
|
||||||
|
target_pid = 0
|
||||||
|
self_hash = ""
|
||||||
|
target_image_path = ""
|
||||||
|
target_hash = ""
|
||||||
|
raw_json_log = json.loads(raw_log)
|
||||||
|
|
||||||
|
if current_process is not None:
|
||||||
|
chain_hash = current_process.chain.hash
|
||||||
|
parent_pid = current_process.ppid
|
||||||
|
if "TargetProcessId" in raw_json_log:
|
||||||
|
target_process: process.Process = current_process.chain.find_process_by_pid(
|
||||||
|
raw_json_log["TargetProcessId"]
|
||||||
|
)
|
||||||
|
target_pid = target_process.pid
|
||||||
|
target_image_path = target_process.path
|
||||||
|
target_hash = target_process.md5
|
||||||
|
self_hash = current_process.md5
|
||||||
|
|
||||||
|
sql.push_process_raw(
|
||||||
|
host,
|
||||||
|
raw_json_log,
|
||||||
|
rule_hit_name,
|
||||||
|
score,
|
||||||
|
chain_hash,
|
||||||
|
had_threat,
|
||||||
|
parent_pid,
|
||||||
|
target_pid,
|
||||||
|
self_hash,
|
||||||
|
target_image_path,
|
||||||
|
target_hash,
|
||||||
|
params,
|
||||||
|
user,
|
||||||
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
for iter in process.g_ProcessChainList:
|
||||||
|
item: process.Process = iter
|
||||||
|
if item.risk_score >= config.MAX_THREAT_SCORE:
|
||||||
|
item.print_process()
|
||||||
|
"""
|
||||||
111
Server/plugin.py
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
from imp import find_module, load_module
|
||||||
|
import global_vars
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def reload_plugs():
|
||||||
|
for index in range(len(global_vars.g_plugs)):
|
||||||
|
_, plug_obj = global_vars.g_plugs[index]
|
||||||
|
del sys.modules[plug_obj.__name__]
|
||||||
|
del plug_obj
|
||||||
|
global_vars.g_plugs = walk_path_get_plugs(
|
||||||
|
global_vars.PLUGS_PATH)
|
||||||
|
|
||||||
|
|
||||||
|
def walk_path_get_plugs(pPath):
|
||||||
|
plugs = []
|
||||||
|
for root, dirs, files in os.walk(pPath):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith(".py"):
|
||||||
|
module_name = file[:-3]
|
||||||
|
plugs_path = os.path.join(root, file)
|
||||||
|
if module_name not in sys.modules:
|
||||||
|
file_handle, file_path, dect = find_module(
|
||||||
|
module_name, [root])
|
||||||
|
try:
|
||||||
|
print("load module:", module_name)
|
||||||
|
module_obj = load_module(
|
||||||
|
module_name, file_handle, file_path, dect)
|
||||||
|
print("load module_obj:", module_obj)
|
||||||
|
if hasattr(module_obj, "rm_plugs_config") == False \
|
||||||
|
or hasattr(module_obj, "plugin_init") == False \
|
||||||
|
or 'author' not in module_obj.rm_plugs_config.keys() \
|
||||||
|
or 'description' not in module_obj.rm_plugs_config.keys() \
|
||||||
|
or 'version' not in module_obj.rm_plugs_config.keys() \
|
||||||
|
or "enable" in module_obj.rm_plugs_config.keys() and module_obj.rm_plugs_config['enable'] == False:
|
||||||
|
del module_obj
|
||||||
|
del sys.modules[module_name]
|
||||||
|
continue
|
||||||
|
print('----------------------------------')
|
||||||
|
print('加载模块: {} 模块作者: {} 模块介绍: {} 版本: {}'.format(
|
||||||
|
module_name, module_obj.rm_plugs_config['author'], module_obj.rm_plugs_config['description'], module_obj.rm_plugs_config['version']))
|
||||||
|
plugs.append((plugs_path, module_obj))
|
||||||
|
module_obj.plugin_init()
|
||||||
|
finally:
|
||||||
|
if file_handle:
|
||||||
|
file_handle.close()
|
||||||
|
return plugs
|
||||||
|
|
||||||
|
|
||||||
|
def dispath_process_terminal(host, current_process, raw_log_data, json_log_data):
|
||||||
|
for index in range(len(global_vars.g_plugs)):
|
||||||
|
_, plug_obj = global_vars.g_plugs[index]
|
||||||
|
if hasattr(plug_obj, "process_terminal"):
|
||||||
|
plug_obj.process_terminal(
|
||||||
|
current_process, host, raw_log_data, json_log_data)
|
||||||
|
|
||||||
|
|
||||||
|
def dispath_rule_new_process_create(host, current_process, raw_log_data, json_log_data):
|
||||||
|
threat_type = global_vars.THREAT_TYPE_NONE
|
||||||
|
for index in range(len(global_vars.g_plugs)):
|
||||||
|
_, plug_obj = global_vars.g_plugs[index]
|
||||||
|
if hasattr(plug_obj, "rule_new_process_create"):
|
||||||
|
if threat_type == global_vars.THREAT_TYPE_NONE:
|
||||||
|
threat_type = plug_obj.rule_new_process_create(
|
||||||
|
current_process, host, raw_log_data, json_log_data)
|
||||||
|
else:
|
||||||
|
plug_obj.rule_new_process_create(
|
||||||
|
current_process, host, raw_log_data, json_log_data)
|
||||||
|
return threat_type
|
||||||
|
|
||||||
|
|
||||||
|
def dispath_rule_new_process_action(host, current_process, raw_log_data, json_log_data):
|
||||||
|
threat_type = global_vars.THREAT_TYPE_NONE
|
||||||
|
for index in range(len(global_vars.g_plugs)):
|
||||||
|
_, plug_obj = global_vars.g_plugs[index]
|
||||||
|
if hasattr(plug_obj, "rule_new_process_action"):
|
||||||
|
if threat_type == global_vars.THREAT_TYPE_NONE:
|
||||||
|
threat_type = plug_obj.rule_new_process_action(
|
||||||
|
current_process, host, raw_log_data, json_log_data)
|
||||||
|
else:
|
||||||
|
plug_obj.rule_new_process_action(
|
||||||
|
current_process, host, raw_log_data, json_log_data)
|
||||||
|
return threat_type
|
||||||
|
|
||||||
|
|
||||||
|
def dispath_rule_init():
|
||||||
|
for index in range(len(global_vars.g_plugs)):
|
||||||
|
_, plug_obj = global_vars.g_plugs[index]
|
||||||
|
if hasattr(plug_obj, "rule_init"):
|
||||||
|
plug_obj.rule_init()
|
||||||
|
|
||||||
|
# 有性能问题,以后再说
|
||||||
|
|
||||||
|
|
||||||
|
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())
|
||||||
|
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()
|
||||||
|
return 'Access Denied '
|
||||||
43
Server/plugins/helloworld/helloworld.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import global_vars
|
||||||
|
import yara
|
||||||
|
import glob
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
rm_plugs_config = {
|
||||||
|
"enable": False,
|
||||||
|
"author": "huoji",
|
||||||
|
"description": "hello world插件示例",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"html": "helloworld"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def html_menu():
|
||||||
|
# https://fonts.google.com/icons?selected=Material+Icons
|
||||||
|
return {'name': "示例插件", 'icon': 'lightbulb', 'html': rm_plugs_config['html']}
|
||||||
|
|
||||||
|
|
||||||
|
def html_draw():
|
||||||
|
return '<div>hello world</div>'
|
||||||
|
|
||||||
|
|
||||||
|
def process_terminal(current_process, host, raw_log_data, json_log_data):
|
||||||
|
print('[helloworld plugin] rule new process create')
|
||||||
|
|
||||||
|
|
||||||
|
def rule_new_process_create(current_process, host, raw_log_data, json_log_data):
|
||||||
|
print('[helloworld plugin] rule new process create')
|
||||||
|
return global_vars.THREAT_TYPE_NONE
|
||||||
|
|
||||||
|
|
||||||
|
def rule_new_process_action(current_process, host, raw_log_data, json_log_data):
|
||||||
|
print('[helloworld plugin] rule new process action')
|
||||||
|
return global_vars.THREAT_TYPE_NONE
|
||||||
|
|
||||||
|
|
||||||
|
def rule_init():
|
||||||
|
print('[helloworld plugin] rule init')
|
||||||
|
|
||||||
|
|
||||||
|
def plugin_init():
|
||||||
|
print('[helloworld plugin] plugin init')
|
||||||
313
Server/process.py
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
|
||||||
|
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',
|
||||||
|
'c:\\program files (x86)\\google\\update\\googleupdate.exe',
|
||||||
|
"c:\\program files\\google\\chrome\\application\\chrome.exe",
|
||||||
|
"d:\\programs\\microsoft vs code\\code.exe",
|
||||||
|
"c:\\windows\\temp\\inv4cdf_tmp\\invcol.exe",
|
||||||
|
"d:\\program files (x86)\\microsoft visual studio\\2019\\community\\common7\\ide\\devenv.exe",
|
||||||
|
"c:\\program files\\dell\\saremediation\\agent\\dellsupportassistremedationservice.exe",
|
||||||
|
"d:\\program files (x86)\\microsoft visual studio\\2019\\community\\common7\\ide\\extensions\\microsoft\\liveshare\\agent\\vsls-agent.exe",
|
||||||
|
"d:\\program files (x86)\\microsoft visual studio\\2019\\community\\common7\\servicehub\\controller\\microsoft.servicehub.controller.exe ",
|
||||||
|
"d:\\program files (x86)\\microsoft visual studio\\2019\\community\\vc\\tools\\msvc\\14.29.30133\\bin\\hostx86\\x64\\cl.exe",
|
||||||
|
"c:\\program files\\git\\mingw64\\bin\\git.exe",
|
||||||
|
"c:\\windows\\system32\\usoclient.exe",
|
||||||
|
"c:\\windows\\system32\\winlogon.exe",
|
||||||
|
"c:\\windows\\system32\\userinit.exe",
|
||||||
|
"c:\\windows\\system32\\dwm.exe",
|
||||||
|
"c:\\windows\\system32\\compattelrunner.exe",
|
||||||
|
"c:\\windows\\system32\\searchindexer.exe",
|
||||||
|
"c:\\windows\\system32\\searchprotocolhost.exe",
|
||||||
|
'c:\\windows\\system32\\runtimebroker.exe',
|
||||||
|
'c:\\windows\\system32\\backgroundtaskhost.exe',
|
||||||
|
'c:\\program files\\dell\\supportassistagent\\pcd\\supportassist\\dsapi.exe',
|
||||||
|
'c:\\program files\\dell\\supportassistagent\\pcd\\supportassist\\systemidlecheck.exe',
|
||||||
|
'c:\\program files (x86)\\microsoft\\edgeupdate\\microsoftedgeupdate.exe',
|
||||||
|
'c:\\program files\\common files\\mcafee\\platform\\core\\mchost.exe',
|
||||||
|
'c:\\windows\\system32\\mmc.exe',
|
||||||
|
'c:\\program files (x86)\\microsoft\\edge\\application\\101.0.1210.53\\identity_helper.exe',
|
||||||
|
'c:\\windows\\system32\\audiodg.exe',
|
||||||
|
'c:\\windows\\system32\\smartscreen.exe',
|
||||||
|
'c:\\program files\\rmroot\\rm_service.exe',
|
||||||
|
'c:\\windows\\immersivecontrolpanel\\systemsettings.exe',
|
||||||
|
'c:\\program files (x86)\\microsoft\\edge\\application\\msedge.exe',
|
||||||
|
'c:\\users\\localhost\\appdata\\local\\programs\\microsoft vs code\\code.exe',
|
||||||
|
'c:\\program files (x86)\\aliwangwang\\aliim.exe',
|
||||||
|
'c:\\program files\\git\\cmd\\git.exe',
|
||||||
|
'c:\\windows\\system32\\taskmgr.exe',
|
||||||
|
'd:\\tools\\microsoft vs code\\code.exe']
|
||||||
|
trust_list = [
|
||||||
|
['node.exe', 'wmic.exe', 'conhost.exe', 'powershell.exe'],
|
||||||
|
['tqclient.exe', 'wscavctrl.exe', 'regsvr32.exe', 'dumpuper.exe'],
|
||||||
|
['tqclient.exe', 'tqassetregister.exe', 'wscavctrl.exe'],
|
||||||
|
['code.exe', 'conhost.exe', 'bash.exe', 'powershell.exe', 'go.exe'],
|
||||||
|
['code.exe', 'conhost.exe', 'bash.exe', 'powershell.exe'],
|
||||||
|
['explorer.exe', 'thunder.exe', 'xlliveud.exe', 'aplayer.exe'],
|
||||||
|
['ddvdatacollector.exe',
|
||||||
|
'atiw.exe'],
|
||||||
|
]
|
||||||
|
skip_md5 = [
|
||||||
|
'82bcb342bce193dfe1740a13bce62e81',
|
||||||
|
'406b23ca616e3ba6cf6033934ff073fc',
|
||||||
|
'c9d7fa5d48de4f3b9615595c336f6bdb',
|
||||||
|
'0cff71e27df7f00fb1f029920bd8869a',
|
||||||
|
'249a55048751d0c77446657437c342b7',
|
||||||
|
'452012f093d716c17c5cf93e31dd075a',
|
||||||
|
'b8ba559709e05485ce9ee39c5a028e30',
|
||||||
|
'cb83db7acb08ccd0370200eed9a1803b',
|
||||||
|
'cde7786dba838941e42814f611be4fcd',
|
||||||
|
'0b50aa0f894d6a65f3fd749cb0c6a5f2',
|
||||||
|
'0d46559e826c8a7b5d432d0a91954ba2',
|
||||||
|
'c07447a5b870e76bafa14ea2b39282c2',
|
||||||
|
'b55ad19c6c110e9bf985bc8674f7bcb3',
|
||||||
|
'c69459ddbf5c2114bfd70b170b8807e0',
|
||||||
|
'c8e806dd1d44c6993b6d85fa77d9f89f',
|
||||||
|
'b9dca65ce1540b8679bc9112ea100032',
|
||||||
|
'f7017525f394d84ce1b727f50244a9ce',
|
||||||
|
'32275787c7c51d2310b8fe2facf2a935',
|
||||||
|
'1acf25a85a4e0a9b7da5d948ca2a69b4',
|
||||||
|
'84244433fa7b5b80d0b7f5abd88eb8d6',
|
||||||
|
'ebe463f5bc61aa2d44e698b6e06df705',
|
||||||
|
'f7c71796dab2a6077458e038d1274392'
|
||||||
|
]
|
||||||
|
root_process_path = ['c:\\windows\system32\\services.exe',
|
||||||
|
'c:\\windows\system32\\svchost.exe',
|
||||||
|
'c:\\windows\\explorer.exe',
|
||||||
|
'c:\\windows\\system32\\wbem\\wmiprvse.exe']
|
||||||
|
g_ProcessChainList = []
|
||||||
|
|
||||||
|
# chain
|
||||||
|
# -> (pid,ppid)
|
||||||
|
# -> (pid,ppid)
|
||||||
|
|
||||||
|
|
||||||
|
class Process:
|
||||||
|
def __init__(self, pid, ppid, path, params, time, md5, user, host):
|
||||||
|
self.pid = pid
|
||||||
|
self.ppid = ppid
|
||||||
|
self.path = path
|
||||||
|
self.params = params
|
||||||
|
self.chain_hash = ''
|
||||||
|
self.active = True
|
||||||
|
self.operationlist = {}
|
||||||
|
self.risk_score = 0
|
||||||
|
self.terminate = False
|
||||||
|
self.rmpid = tools.get_md5(
|
||||||
|
str(pid) + str(ppid) + path + params + str(time))
|
||||||
|
self.time = time
|
||||||
|
self.rmppid = ""
|
||||||
|
self.root_rmpid = ""
|
||||||
|
self.md5 = md5
|
||||||
|
self.user = user
|
||||||
|
self.chain: ProcessChain = None
|
||||||
|
self.host = host
|
||||||
|
|
||||||
|
def set_chain_data(self, chain):
|
||||||
|
self.chain = chain
|
||||||
|
|
||||||
|
def set_chain_hash(self, chain_hash):
|
||||||
|
self.chain_hash = chain_hash
|
||||||
|
|
||||||
|
def set_root_rmpid(self, root_rmpid):
|
||||||
|
self.root_rmpid = root_rmpid
|
||||||
|
|
||||||
|
def set_rmppid(self, rmppid):
|
||||||
|
self.rmppid = rmppid
|
||||||
|
|
||||||
|
def set_score(self, new_score, opertion):
|
||||||
|
if opertion not in self.operationlist:
|
||||||
|
self.risk_score += new_score
|
||||||
|
self.operationlist[opertion] = 1
|
||||||
|
else:
|
||||||
|
self.operationlist[opertion] += 1
|
||||||
|
|
||||||
|
if opertion not in self.chain.operationlist:
|
||||||
|
self.chain.risk_score += new_score
|
||||||
|
self.chain.operationlist[opertion] = 1
|
||||||
|
else:
|
||||||
|
self.chain.operationlist[opertion] += 1
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessChain:
|
||||||
|
def __init__(self, root_process: Process):
|
||||||
|
# 这样的话 无论几分钟读取都是固定关掉chain hash
|
||||||
|
self.hash = tools.get_md5(root_process.rmpid + str(root_process.time))
|
||||||
|
self.root_process_rmid = root_process.rmpid
|
||||||
|
self.root_process = root_process
|
||||||
|
self.active_count = 0
|
||||||
|
self.terminate_count = 0
|
||||||
|
self.risk_score = 0
|
||||||
|
self.operationlist = {}
|
||||||
|
self.process_list = []
|
||||||
|
self.json_arrays = []
|
||||||
|
self.active = True
|
||||||
|
self.rpc = False
|
||||||
|
self.rpc_process_chain = ""
|
||||||
|
self.time = root_process.time
|
||||||
|
self.host = root_process.host
|
||||||
|
self.add_root_process(root_process)
|
||||||
|
|
||||||
|
def get_operationlist(self):
|
||||||
|
return self.operationlist
|
||||||
|
|
||||||
|
def find_process_by_pid(self, pid):
|
||||||
|
for iter in self.process_list:
|
||||||
|
process_item: Process = iter
|
||||||
|
if process_item.pid == pid and process_item.active:
|
||||||
|
return process_item
|
||||||
|
return None
|
||||||
|
|
||||||
|
def add_root_process(self, root_process: Process):
|
||||||
|
root_process.set_chain_hash(self.hash)
|
||||||
|
root_process.set_rmppid(root_process.rmpid)
|
||||||
|
root_process.set_chain_data(self)
|
||||||
|
self.process_list.append(root_process)
|
||||||
|
self.active_count += 1
|
||||||
|
|
||||||
|
def add_process(self, new_process: Process, new_ppid):
|
||||||
|
parent_process = self.find_process_by_pid(new_ppid)
|
||||||
|
if parent_process is None:
|
||||||
|
return
|
||||||
|
new_process.set_rmppid(parent_process.rmpid)
|
||||||
|
new_process.set_chain_hash(self.hash)
|
||||||
|
new_process.set_root_rmpid(self.root_process_rmid)
|
||||||
|
new_process.set_chain_data(self)
|
||||||
|
self.process_list.append(new_process)
|
||||||
|
self.active_count += 1
|
||||||
|
|
||||||
|
def terminal_process(self, terminal_pid):
|
||||||
|
process = self.find_process_by_pid(terminal_pid)
|
||||||
|
if process is None:
|
||||||
|
return
|
||||||
|
process.terminate = True
|
||||||
|
self.terminate_count += 1
|
||||||
|
if self.terminate_count == self.active_count:
|
||||||
|
self.active = False
|
||||||
|
|
||||||
|
def print_node(self, node, level):
|
||||||
|
print((" " * level) + "|--" +
|
||||||
|
node["path"] + " 进程pid: (" + str(node["pid"]) + ") 进程ppid: (" + str(node["ppid"]) + ")进程命令行: (" + node["params"] + ") 进程hash: (" + str(node["md5"]) + ") 触发规则: " + str(node["operationlist"]) + " 进程活动:" + str(node['active']))
|
||||||
|
for child in node["children"]:
|
||||||
|
self.print_node(child, level + 1)
|
||||||
|
|
||||||
|
def save_to_json(self, node):
|
||||||
|
self.json_arrays = node
|
||||||
|
|
||||||
|
def get_json(self):
|
||||||
|
return json.dumps({'process_node': self.json_arrays})
|
||||||
|
|
||||||
|
def clear_json(self):
|
||||||
|
self.json_arrays = []
|
||||||
|
|
||||||
|
def print_process(self):
|
||||||
|
self.print_node(self.json_arrays, 0)
|
||||||
|
|
||||||
|
def update_process_tree(self):
|
||||||
|
# print('========================================================')
|
||||||
|
# print('进程链hash: {} 进程链等级: {} 触发的行为列表 {}'.format(
|
||||||
|
# self.hash, self.risk_score, self.operationlist))
|
||||||
|
pid_nodes = []
|
||||||
|
for proc_info in self.process_list:
|
||||||
|
node = [info for info in pid_nodes if info["rmpid"] ==
|
||||||
|
proc_info.rmpid]
|
||||||
|
node = node[0] if len(node) > 0 else None
|
||||||
|
|
||||||
|
parent_node = [
|
||||||
|
info for info in pid_nodes if info["rmpid"] == proc_info.rmppid]
|
||||||
|
parent_node = parent_node[0] if len(parent_node) > 0 else None
|
||||||
|
|
||||||
|
if node is None:
|
||||||
|
node = {
|
||||||
|
"path": proc_info.path,
|
||||||
|
"pid": proc_info.pid,
|
||||||
|
"ppid": proc_info.ppid,
|
||||||
|
"rmpid": proc_info.rmpid,
|
||||||
|
"rmppid": proc_info.rmppid,
|
||||||
|
"params": proc_info.params,
|
||||||
|
"operationlist": proc_info.operationlist,
|
||||||
|
"md5": proc_info.md5,
|
||||||
|
"active": proc_info.active,
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
pid_nodes.append(node)
|
||||||
|
|
||||||
|
if parent_node is None and proc_info.rmppid != proc_info.rmpid:
|
||||||
|
target_info = next(
|
||||||
|
temp_info for temp_info in self.process_list if temp_info.rmpid == proc_info.rmppid)
|
||||||
|
parent_node = dict()
|
||||||
|
parent_node["active"] = target_info.active
|
||||||
|
parent_node["path"] = target_info.path
|
||||||
|
parent_node["ppid"] = target_info.ppid
|
||||||
|
parent_node["pid"] = target_info.pid
|
||||||
|
parent_node["rmpid"] = target_info.rmpid
|
||||||
|
parent_node["rmppid"] = target_info.rmppid
|
||||||
|
parent_node["md5"] = target_info.md5
|
||||||
|
parent_node["params"] = target_info.params
|
||||||
|
parent_node["operationlist"] = target_info.operationlist
|
||||||
|
parent_node["children"] = []
|
||||||
|
pid_nodes.append(parent_node)
|
||||||
|
|
||||||
|
if parent_node is not None and parent_node["rmpid"] != node["rmpid"]:
|
||||||
|
parent_node["children"].append(node)
|
||||||
|
|
||||||
|
# find root node in pid_nodes
|
||||||
|
root_node = next(
|
||||||
|
info for info in pid_nodes if info["rmpid"] == self.root_process_rmid)
|
||||||
|
#self.print_node(root_node, 0)
|
||||||
|
self.save_to_json(root_node)
|
||||||
|
|
||||||
|
|
||||||
|
def chain_in_trust_list(chain: ProcessChain):
|
||||||
|
# 整个进程链中如果存在(只是存在就行)这些进程就排除
|
||||||
|
global trust_list
|
||||||
|
global root_process_path
|
||||||
|
is_trust = True
|
||||||
|
for trust_process_array in trust_list:
|
||||||
|
is_trust = True
|
||||||
|
for trust_process in trust_process_array:
|
||||||
|
for iter in chain.process_list:
|
||||||
|
process: Process = iter
|
||||||
|
if process.path.find(trust_process) == -1 and process.path not in root_process_path:
|
||||||
|
is_trust = False
|
||||||
|
break
|
||||||
|
if is_trust == False:
|
||||||
|
break
|
||||||
|
if is_trust:
|
||||||
|
break
|
||||||
|
return is_trust
|
||||||
|
|
||||||
|
|
||||||
|
def create_chain(root_process: Process) -> ProcessChain:
|
||||||
|
global g_ProcessChainList
|
||||||
|
chain = ProcessChain(root_process)
|
||||||
|
g_ProcessChainList.append(chain)
|
||||||
|
return chain
|
||||||
|
|
||||||
|
|
||||||
|
def get_process_by_pid(pid) -> Process:
|
||||||
|
chain_item = get_process_chain_by_pid(pid)
|
||||||
|
if chain_item is None:
|
||||||
|
return None
|
||||||
|
return chain_item.find_process_by_pid(pid)
|
||||||
|
|
||||||
|
|
||||||
|
def set_process_terminal_by_pid(pid) -> None:
|
||||||
|
chain_item = get_process_chain_by_pid(pid)
|
||||||
|
if chain_item is None:
|
||||||
|
return
|
||||||
|
chain_item.terminal_process(pid)
|
||||||
|
|
||||||
|
|
||||||
|
def get_process_chain_by_pid(pid) -> ProcessChain:
|
||||||
|
for iter in g_ProcessChainList:
|
||||||
|
chain_item: ProcessChain = iter
|
||||||
|
if chain_item.active:
|
||||||
|
process_item = chain_item.find_process_by_pid(pid)
|
||||||
|
if process_item is not None:
|
||||||
|
return chain_item
|
||||||
|
return None
|
||||||
82
Server/rule.py
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import rule_engine
|
||||||
|
import rules.py.process as rule_process
|
||||||
|
import rules.py.action as rule_action
|
||||||
|
import plugin
|
||||||
|
g_sample_rule = {}
|
||||||
|
g_sample_rule['process'] = rule_process.rule
|
||||||
|
g_sample_rule['action'] = rule_action.rule
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
return 0, ''
|
||||||
|
|
||||||
|
|
||||||
|
def calc_score_in_create_process(log):
|
||||||
|
global base_process_rules
|
||||||
|
for iter in base_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:
|
||||||
|
for rule in iter['rules']:
|
||||||
|
# 这是or
|
||||||
|
if rule.matches(log):
|
||||||
|
return iter['score'], iter['name']
|
||||||
|
return 0, ''
|
||||||
|
|
||||||
|
|
||||||
|
def init_rule():
|
||||||
|
global base_process_rules
|
||||||
|
global base_action_rules
|
||||||
|
global base_host_rules
|
||||||
|
for iter in g_sample_rule['process']:
|
||||||
|
temp_process_rules = []
|
||||||
|
for iter_i in iter['rules']:
|
||||||
|
print(iter_i)
|
||||||
|
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']:
|
||||||
|
temp_process_rules = []
|
||||||
|
for iter_i in iter['rules']:
|
||||||
|
print(iter_i)
|
||||||
|
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']:
|
||||||
|
temp_process_rules = []
|
||||||
|
for iter_i in iter['rules']:
|
||||||
|
print(iter_i)
|
||||||
|
temp_process_rules.append(rule_engine.Rule(
|
||||||
|
iter_i
|
||||||
|
))
|
||||||
|
base_host_rules.append(
|
||||||
|
{'name': iter['name'], 'score': iter['score'], 'rules': temp_process_rules})
|
||||||
|
'''
|
||||||
|
plugin.dispath_rule_init()
|
||||||
|
print('init rule done')
|
||||||
165
Server/rules/py/action.py
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
rule = [
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "processaccess" and targetimage =~ ".*lsass.exe" and grantedaccess & 0x0010 and sourceimage =~ ".*rundll32.exe"',
|
||||||
|
],
|
||||||
|
'score': 300,
|
||||||
|
'name': '已知内存加载mimikazt行为'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "processaccess" and targetimage =~ ".*lsass.exe"',
|
||||||
|
],
|
||||||
|
'score': 60,
|
||||||
|
'name': 'LSASS高权限访问'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'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,
|
||||||
|
'name': '异常进程访问'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "processaccess" and sourceimage =~ ".*office16.*" and calltrace =~ ".*kernelbase\.dll.*"',
|
||||||
|
],
|
||||||
|
'score': 100,
|
||||||
|
'name': 'office异常进程内存'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'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.*"',
|
||||||
|
],
|
||||||
|
'score': 40,
|
||||||
|
'name': '不正常的进程访问'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'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_.*"',
|
||||||
|
],
|
||||||
|
'score': 300,
|
||||||
|
'name': '已知CobalStrike'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "pipecreate" and pipename =~ ".*paexec.*"',
|
||||||
|
'action == "pipecreate" and pipename =~ ".*remcom.*"',
|
||||||
|
'action == "pipecreate" and pipename =~ ".*csexec.*"'
|
||||||
|
],
|
||||||
|
'score': 300,
|
||||||
|
'name': '已知内网横向工具'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "pipecreate" and pipename =~ ".*lsadump.*"',
|
||||||
|
'action == "pipecreate" and pipename =~ ".*cachedump.*"',
|
||||||
|
'action == "pipecreate" and pipename =~ ".*wceservicepipe.*"'
|
||||||
|
],
|
||||||
|
'score': 300,
|
||||||
|
'name': '已知mimikazt内存dump'
|
||||||
|
},
|
||||||
|
# todo 懒得做详细的规则了.加油完善规则吧
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "createremotethread"',
|
||||||
|
],
|
||||||
|
'score': 60,
|
||||||
|
'name': '疑似远程线程注入'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "filecreatestreamhash"',
|
||||||
|
],
|
||||||
|
'score': 100,
|
||||||
|
'name': '文件流创建'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "registryadd"',
|
||||||
|
'action == "registryvalueSet"',
|
||||||
|
'action == "registryobjectSet"',
|
||||||
|
],
|
||||||
|
'score': 100,
|
||||||
|
'name': '可疑注册表访问'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "dnsquery"',
|
||||||
|
],
|
||||||
|
'score': 30,
|
||||||
|
'name': 'DNS解析'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "networkconnect"',
|
||||||
|
],
|
||||||
|
'score': 30,
|
||||||
|
'name': '可疑网络链接'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "clipboardchange"',
|
||||||
|
],
|
||||||
|
'score': 30,
|
||||||
|
'name': '可疑剪切板访问'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "processtampering"',
|
||||||
|
],
|
||||||
|
'score': 200,
|
||||||
|
'name': '进程执行流劫持'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "filedeletedetected"',
|
||||||
|
],
|
||||||
|
'score': 50,
|
||||||
|
'name': '删除可执行文件'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'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"',
|
||||||
|
],
|
||||||
|
'score': 80,
|
||||||
|
'name': '在windows目录创建可执行文件'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "filecreate" and targetfilename =~ "c:\\\\\\\\windows\\\\\\\\.*"',
|
||||||
|
],
|
||||||
|
'score': 50,
|
||||||
|
'name': '在C盘目录创建文件'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'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"',
|
||||||
|
],
|
||||||
|
'score': 30,
|
||||||
|
'name': '在appdata目录创建可执行文件'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'rules': [
|
||||||
|
'action == "filecreate"',
|
||||||
|
],
|
||||||
|
'score': 50,
|
||||||
|
'name': '创建可疑文件'
|
||||||
|
}
|
||||||
|
]
|
||||||
390
Server/rules/py/process.py
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
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被执行'
|
||||||
|
},
|
||||||
|
]
|
||||||
327
Server/sql.py
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy import Column, Integer, String
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
# 引入sqlalchemy中相关模块
|
||||||
|
from sqlalchemy import create_engine, MetaData
|
||||||
|
from sqlalchemy import Column, Integer, String, Table
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from sqlalchemy import delete
|
||||||
|
import json
|
||||||
|
|
||||||
|
g_engine = None
|
||||||
|
g_base = declarative_base()
|
||||||
|
g_metadata = None
|
||||||
|
g_rawdata_table = None
|
||||||
|
g_rawdata_table_ins = None
|
||||||
|
g_threat_table = None
|
||||||
|
g_threat_table_ins = None
|
||||||
|
|
||||||
|
|
||||||
|
class raw_process_log(g_base):
|
||||||
|
__tablename__ = "raw_process_log"
|
||||||
|
# 定义各字段
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
# 主机ip
|
||||||
|
host = Column(String)
|
||||||
|
# 动作
|
||||||
|
# processcreate, processterminal
|
||||||
|
action = Column(String)
|
||||||
|
# 进程路径
|
||||||
|
path = Column(String)
|
||||||
|
# 进程pid
|
||||||
|
pid = Column(Integer)
|
||||||
|
# 父进程pid
|
||||||
|
ppid = Column(Integer)
|
||||||
|
# 目标文件路径(如果有)
|
||||||
|
target_path = Column(String)
|
||||||
|
# 目标进程路径(如果有)
|
||||||
|
target_image_path = Column(String)
|
||||||
|
# 目标进程pid(如果有)
|
||||||
|
target_image_pid = Column(Integer)
|
||||||
|
# 目标的hash(如果有)
|
||||||
|
target_hash = Column(String)
|
||||||
|
# hash
|
||||||
|
hash = Column(String)
|
||||||
|
hit = Column(String)
|
||||||
|
score = Column(Integer)
|
||||||
|
chain_hash = Column(String)
|
||||||
|
type = Column(Integer)
|
||||||
|
# 时间戳
|
||||||
|
timestamp = Column(Integer)
|
||||||
|
commandline = Column(String)
|
||||||
|
user = Column(String)
|
||||||
|
# 原始字段
|
||||||
|
data = Column(String)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.id
|
||||||
|
|
||||||
|
|
||||||
|
class threat_log(g_base):
|
||||||
|
__tablename__ = "threat_log"
|
||||||
|
# 定义各字段
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
# 主机ip
|
||||||
|
host = Column(String)
|
||||||
|
# 进程链hash,其他的为000000
|
||||||
|
process_chain_hash = Column(String)
|
||||||
|
# type
|
||||||
|
type = Column(Integer)
|
||||||
|
# 分数
|
||||||
|
risk_score = Column(Integer)
|
||||||
|
# 命中的规则
|
||||||
|
hit_rule = Column(String)
|
||||||
|
# json字段
|
||||||
|
data = Column(String)
|
||||||
|
# 时间戳
|
||||||
|
timestamp = Column(Integer)
|
||||||
|
# is end
|
||||||
|
is_end = Column(Integer)
|
||||||
|
# start process
|
||||||
|
start_process_info = Column(String)
|
||||||
|
# handle type
|
||||||
|
handle_type = Column(Integer)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.id
|
||||||
|
|
||||||
|
|
||||||
|
def init():
|
||||||
|
global g_engine
|
||||||
|
global g_base
|
||||||
|
global g_metadata
|
||||||
|
global g_rawdata_table
|
||||||
|
global g_rawdata_table_ins
|
||||||
|
global g_threat_table
|
||||||
|
global g_threat_table_ins
|
||||||
|
|
||||||
|
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)
|
||||||
|
g_rawdata_table_ins = g_rawdata_table.insert()
|
||||||
|
|
||||||
|
g_threat_table = Table("threat_log", g_metadata, autoload=True)
|
||||||
|
g_threat_table_ins = g_threat_table.insert()
|
||||||
|
|
||||||
|
|
||||||
|
def push_process_raw(
|
||||||
|
host,
|
||||||
|
log,
|
||||||
|
rule_hit_name,
|
||||||
|
score,
|
||||||
|
chain_hash,
|
||||||
|
type,
|
||||||
|
parent_pid,
|
||||||
|
target_pid,
|
||||||
|
self_hash,
|
||||||
|
target_image_path,
|
||||||
|
target_hash,
|
||||||
|
commandline,
|
||||||
|
user,
|
||||||
|
):
|
||||||
|
global g_engine
|
||||||
|
global g_rawdata_table
|
||||||
|
global g_rawdata_table_ins
|
||||||
|
timestamp = int(round(time.time() * 1000))
|
||||||
|
# 偷懒了 有时间再重构
|
||||||
|
ins = g_rawdata_table_ins.values(
|
||||||
|
host=host,
|
||||||
|
action=log["Action"],
|
||||||
|
path=log["Data"]["Path"]
|
||||||
|
if "Path" in log["Data"]
|
||||||
|
else (
|
||||||
|
log["Data"]["SourceImage"]
|
||||||
|
if "SourceImage" in log["Data"]
|
||||||
|
else (log["Data"]["Image"] if "Image" in log["Data"] else "")
|
||||||
|
), # 只有三种情况,没有path就找sourceimage,没有sourceimage就找image
|
||||||
|
pid=log["Data"]["ProcessId"],
|
||||||
|
ppid=parent_pid,
|
||||||
|
target_path=log["Data"]["TargetImage"]
|
||||||
|
if "TargetImage" in log["Data"]
|
||||||
|
else target_image_path,
|
||||||
|
target_image_path=log["Data"]["TargetFilename"]
|
||||||
|
if "TargetFilename" in log["Data"]
|
||||||
|
else "",
|
||||||
|
target_image_pid=target_pid,
|
||||||
|
target_hash=target_hash,
|
||||||
|
hash=self_hash,
|
||||||
|
data=json.dumps(log["Data"]),
|
||||||
|
timestamp=timestamp,
|
||||||
|
hit=rule_hit_name,
|
||||||
|
score=score,
|
||||||
|
chain_hash=chain_hash,
|
||||||
|
commandline=commandline,
|
||||||
|
user=user,
|
||||||
|
type=type,
|
||||||
|
)
|
||||||
|
# 连接引擎
|
||||||
|
conn = g_engine.connect()
|
||||||
|
# 执行语句
|
||||||
|
result = conn.execute(ins)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def select_create_process_raw_log_by_time(start, end):
|
||||||
|
global g_rawdata_table
|
||||||
|
sql_session = sessionmaker(bind=g_engine)
|
||||||
|
raw_log = (
|
||||||
|
sql_session()
|
||||||
|
.query(g_rawdata_table)
|
||||||
|
.filter(
|
||||||
|
raw_process_log.timestamp >= start,
|
||||||
|
raw_process_log.timestamp < end,
|
||||||
|
raw_process_log.action == "processcreate",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
sql_session().close()
|
||||||
|
return raw_log
|
||||||
|
|
||||||
|
|
||||||
|
def select_threat_by_chain_id(host, process_chain_hash, type):
|
||||||
|
global g_threat_table
|
||||||
|
sql_session = sessionmaker(bind=g_engine)
|
||||||
|
threat = (
|
||||||
|
sql_session()
|
||||||
|
.query(g_threat_table)
|
||||||
|
.filter_by(host=host, process_chain_hash=process_chain_hash, type=type)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
sql_session().close()
|
||||||
|
return threat
|
||||||
|
|
||||||
|
|
||||||
|
def update_threat_log(
|
||||||
|
host, risk_score, hit_rule_json, process_chain_hash, raw_json, type, is_end
|
||||||
|
):
|
||||||
|
global g_threat_table
|
||||||
|
global g_engine
|
||||||
|
conn = g_engine.connect()
|
||||||
|
update = (
|
||||||
|
g_threat_table.update()
|
||||||
|
.values(
|
||||||
|
risk_score=risk_score,
|
||||||
|
hit_rule=hit_rule_json,
|
||||||
|
data=raw_json,
|
||||||
|
is_end=int(is_end),
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
g_threat_table.columns.host == host,
|
||||||
|
g_threat_table.columns.process_chain_hash == process_chain_hash,
|
||||||
|
g_threat_table.columns.type == type,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
result = conn.execute(update)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def handle_threat_log(threat_id, handle_type):
|
||||||
|
global g_threat_table
|
||||||
|
global g_engine
|
||||||
|
conn = g_engine.connect()
|
||||||
|
update = (
|
||||||
|
g_threat_table.update()
|
||||||
|
.values(handle_type=handle_type, is_end=1)
|
||||||
|
.where(g_threat_table.columns.id == int(threat_id))
|
||||||
|
)
|
||||||
|
result = conn.execute(update)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def delete_threat(threat_id):
|
||||||
|
global g_threat_table
|
||||||
|
global g_engine
|
||||||
|
conn = g_engine.connect()
|
||||||
|
result = conn.execute(
|
||||||
|
delete(g_threat_table).where(g_threat_table.columns.id == int(threat_id))
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def query_one_threat(threat_id):
|
||||||
|
global g_threat_table
|
||||||
|
sql_session = sessionmaker(bind=g_engine)
|
||||||
|
threat = sql_session().query(g_threat_table).filter_by(id=threat_id).first()
|
||||||
|
sql_session().close()
|
||||||
|
return threat
|
||||||
|
|
||||||
|
|
||||||
|
def query_all_threat_log(query_type):
|
||||||
|
global g_threat_table
|
||||||
|
sql_session = sessionmaker(bind=g_engine)
|
||||||
|
if int(query_type) == -1:
|
||||||
|
threat = (
|
||||||
|
sql_session()
|
||||||
|
.query(g_threat_table)
|
||||||
|
.with_entities(
|
||||||
|
threat_log.host,
|
||||||
|
threat_log.process_chain_hash,
|
||||||
|
threat_log.hit_rule,
|
||||||
|
threat_log.timestamp,
|
||||||
|
threat_log.type,
|
||||||
|
threat_log.risk_score,
|
||||||
|
threat_log.id,
|
||||||
|
threat_log.is_end,
|
||||||
|
threat_log.start_process_info,
|
||||||
|
threat_log.handle_type,
|
||||||
|
)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
threat = (
|
||||||
|
sql_session()
|
||||||
|
.query(g_threat_table)
|
||||||
|
.with_entities(
|
||||||
|
threat_log.host,
|
||||||
|
threat_log.process_chain_hash,
|
||||||
|
threat_log.hit_rule,
|
||||||
|
threat_log.timestamp,
|
||||||
|
threat_log.type,
|
||||||
|
threat_log.risk_score,
|
||||||
|
threat_log.id,
|
||||||
|
threat_log.is_end,
|
||||||
|
threat_log.start_process_info,
|
||||||
|
threat_log.handle_type,
|
||||||
|
)
|
||||||
|
.filter_by(handle_type=query_type)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
sql_session().close()
|
||||||
|
return threat
|
||||||
|
|
||||||
|
|
||||||
|
def push_threat_log(
|
||||||
|
host,
|
||||||
|
risk_score,
|
||||||
|
hit_rule_json,
|
||||||
|
process_chain_hash,
|
||||||
|
raw_json,
|
||||||
|
type,
|
||||||
|
start_process_info,
|
||||||
|
):
|
||||||
|
global g_engine
|
||||||
|
global g_threat_table
|
||||||
|
global g_threat_table_ins
|
||||||
|
ins = g_threat_table_ins.values(
|
||||||
|
host=host,
|
||||||
|
risk_score=risk_score,
|
||||||
|
process_chain_hash=process_chain_hash,
|
||||||
|
hit_rule=hit_rule_json,
|
||||||
|
type=type,
|
||||||
|
data=raw_json,
|
||||||
|
timestamp=int(round(time.time() * 1000)),
|
||||||
|
is_end=0,
|
||||||
|
start_process_info=start_process_info,
|
||||||
|
handle_type=0,
|
||||||
|
)
|
||||||
|
# 连接引擎
|
||||||
|
conn = g_engine.connect()
|
||||||
|
# 执行语句
|
||||||
|
result = conn.execute(ins)
|
||||||
|
# print(raw_json)
|
||||||
|
return result
|
||||||
1
Server/templates/css/950.8be8f613.css
Normal file
@@ -0,0 +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}
|
||||||
0
Server/templates/css/app.31d6cfe0.css
Normal file
6
Server/templates/css/vendor.5b8581f0.css
Normal file
BIN
Server/templates/css/vendor.5b8581f0.css.gz
Normal file
BIN
Server/templates/favicon.ico
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
Server/templates/fonts/KFOkCnqEu92Fr1MmgVxIIzQ.68bb21d0.woff
Normal file
BIN
Server/templates/fonts/KFOlCnqEu92Fr1MmEU9fBBc-.48af7707.woff
Normal file
BIN
Server/templates/fonts/KFOlCnqEu92Fr1MmSU5fBBc-.c2f7ab22.woff
Normal file
BIN
Server/templates/fonts/KFOlCnqEu92Fr1MmWUlfBBc-.77ecb942.woff
Normal file
BIN
Server/templates/fonts/KFOlCnqEu92Fr1MmYUtfBBc-.f5677eb2.woff
Normal file
BIN
Server/templates/fonts/KFOmCnqEu92Fr1Mu4mxM.f1e2a767.woff
Normal file
BIN
Server/templates/icons/favicon-128x128.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
Server/templates/icons/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 859 B |
BIN
Server/templates/icons/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
Server/templates/icons/favicon-96x96.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
1
Server/templates/index.html
Normal file
@@ -0,0 +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>
|
||||||
1
Server/templates/js/193.aede10b8.js
Normal file
@@ -0,0 +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})}}]);
|
||||||
1
Server/templates/js/64.d71ba94a.js
Normal file
BIN
Server/templates/js/64.d71ba94a.js.gz
Normal file
1
Server/templates/js/904.3892002c.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"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/950.7d796a08.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"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.3ea8aeff.js
Normal file
32
Server/templates/js/vendor.070221f5.js
Normal file
BIN
Server/templates/js/vendor.070221f5.js.gz
Normal file
18
Server/tools.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import hashlib
|
||||||
|
import base64
|
||||||
|
|
||||||
|
|
||||||
|
def base64_deocde(str):
|
||||||
|
try:
|
||||||
|
return base64.b64decode(str).decode('utf-8')
|
||||||
|
except:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def get_md5(password):
|
||||||
|
# 1- 实例化加密对象
|
||||||
|
md5 = hashlib.md5()
|
||||||
|
# 2- 进⾏加密操作
|
||||||
|
md5.update(password.encode('utf-8'))
|
||||||
|
# 3- 返回加密后的结果
|
||||||
|
return md5.hexdigest()
|
||||||
151
Server/webserver.py
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
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 = '>.}'
|
||||||
|
|
||||||
|
|
||||||
|
@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>')
|
||||||
|
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>')
|
||||||
|
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')
|
||||||
|
def plugin_menu():
|
||||||
|
if request.remote_addr not in config.ALLOW_ACCESS_IP:
|
||||||
|
return "Access Denied"
|
||||||
|
return {'data': {'menu': plugin.dispath_html_menu()}}
|
||||||
|
|
||||||
|
|
||||||
|
@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}
|
||||||
|
|
||||||
|
|
||||||
|
@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):
|
||||||
|
return "Access Denied"
|
||||||
|
sql.handle_threat_log(id, handletype)
|
||||||
|
return {'data': {'success': 1}}
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/v1/get/process_chain/delete', methods=['GET'])
|
||||||
|
def delete_chain_data():
|
||||||
|
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}}
|
||||||
|
|
||||||
|
|
||||||
|
@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')
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
return {'data': return_data}
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/v1/get/process_chain/all')
|
||||||
|
def process_chain():
|
||||||
|
# -1全部 0未处理的 1处理的 2忽略的
|
||||||
|
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}
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/v1/process', methods=['POST'])
|
||||||
|
def process():
|
||||||
|
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)
|
||||||
|
|
||||||
|
return {'status': 'success'}
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
plugin.reload_plugs()
|
||||||
|
sql.init()
|
||||||
|
rule.init_rule()
|
||||||
|
|
||||||
|
# 如果你觉得日志太多了,去掉这个注释...
|
||||||
|
flask_log = logging.getLogger('werkzeug')
|
||||||
|
flask_log.setLevel(logging.ERROR)
|
||||||
|
app.run(debug=True, host="0.0.0.0")
|
||||||