ScanWebShell 开发文档
介绍
ScanWebShell 为基于机器学习的PHP-WebShell扫描工具,该版本为web服务形式。支持多用户独立使用和利用celery
来配合扫描任务。 对于机器学习相关的部分,我并不是这一部分的主要负责人。
链接🔗: https://github.com/fe1w0/ScanWebShell
index
job/count
总体设计
后端设计如下:
URL | 视图 | 模板 | 说明 |
---|---|---|---|
/index/ | ScanWebShell.views.index | user/index.html | 主页与项目信息 |
/user/login/ | user.views.login | user/login.html | 登录 |
/user/register/ | user.views.register | user/register.html | 注册 |
/user/logout/ | user.views.logout | 无需专门的页面 | 登出 |
/job/upload | job.views.upload_file | job/upload.html | 上传文件 |
/job/count | job.views.countResult | job/count.html | 显示所有任务信息 |
/job/search/?task_id | job.views.searchResult | job/search.html | 根据task_id 返回任务的详细信息 |
/job/scan?file | job.views.scan_file | job/scan.html | 扫描文件 |
- index 项目信息
- user 与用户相关的处理
- login 登录
- register 登录
- forget 忘记密码
- logut 登出
- job 任务相关处理
- upload 上传webshell文件
- scan 对上传的文件进行扫描
- count 统计当前用户所有的扫描任务与已上传的文件
- search 具体描述某一任务,重点为改任务的结果
其中,仅index
允许游客访问。
环境为开发环境,上传的是生产环境
User 应用设计
后端设计
参考的项目
User 其MVT中的Module
和view
部分,参考于基于Django2.2可重用登录与注册系统
模型如下:
class User(models.Model):
"""
用户模型
"""
name = models.CharField(max_length=128,unique=True)
password = models.CharField(max_length=256)
email = models.EmailField(unique=True)
c_time = models.DateTimeField(auto_now_add=True)
has_confirmed = models.BooleanField(default=False)
def __str__(self):
return self.name
class Meta:
ordering = ["-c_time"]
verbose_name = "用户"
verbose_name_plural = "用户"
class ConfirmString(models.Model):
"""
邮箱确认模型
"""
code = models.CharField(max_length=256)
user = models.OneToOneField('User', on_delete=models.CASCADE)
c_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user.name + ": " + self.code
class Meta:
ordering = ["-c_time"]
verbose_name = "确认码"
verbose_name_plural = "确认码"
其他功能
之后的View
部分是在基于Django2.2可重用登录与注册系统的基础上,补充部分功能:
- 忘记密码
- 重置密码
- django simple captcha refresh
忘记密码
其中重置密码没有独立出来,是属于忘记密码的一部分
相关模型如下:
class ConfirmString(models.Model):
"""
邮箱确认模型
"""
code = models.CharField(max_length=256)
user = models.OneToOneField('User', on_delete=models.CASCADE)
c_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user.name + ": " + self.code
class Meta:
ordering = ["-c_time"]
verbose_name = "确认码"
verbose_name_plural = "确认码"
应用逻辑如下:
- 用户在
user/forget/index
的表单中,添加需要重置密码的用户邮箱 - 若无改用户,则弹出无该用户的警告
- 有该邮箱,则往用户邮箱发送重置密码的链接,此时
- 重置密码的链接大致为
user/forget/confirm/?code=*
- 当code是在
ConfirmString
实例中时,将user
的has_confirmed
,使其在重置密码期间无法登录,之后携带code
转到user/forget/change/?code=*
- 若数据库中没有该
code
,则拒绝 user/forget/change/?code=*
中,根据code
查询一对一匹配的user
,再根据添加的表单修改密码,之后confirm.user.save()
和confirm.delete()
django simple captcha refresh
在原项目基础上,需要修改Template
和urls.py
urls.py
captcha.views 内置就有刷新验证码的方法
from captcha.views import captcha_refresh # 验证码刷新功能,captcha_refresh为captcha.views内置方法,不需要我们单独写
urlpatterns = [
...
path('refresh/', captcha_refresh), # 点击可以刷新验证码
]
Template
{#刷新验证码的脚本,放到body部分的最后面即可#}
<script>
$('.captcha').click(function () {
$.getJSON('/captcha/refresh/',function (result) {
$('.captcha').attr('src',result['image_url']);
$('#id_captcha_0').val(result['key']);
});
});
</script>
前端设计
前端设计上是基本参考于bootstrapdoc 5.0 example.
- index.html
- login.html
Job 应用设计
后端设计
Job
的应用设计上,个人在设计时,分为一些几个功能:
- Upload 上传WebShell 文件
- Count 统计当前用户的上传文件和扫描任务
- Scan 根据
file
文件创建扫描任务 - Search 根据
task_id
查询扫描任务结果
在四个任务中,upload
、count
、search
设计相对简单,网上参考也相对较多,这里只是简单介绍。而scan
中的设计相对麻烦,本质上是利用celery
来处理扫描任务。
upload
在models
中设计相关模型,且添加装饰器,用于在admin
可以方便地同时删除文件对象和磁盘中的文件。
models.py
class ModelWithFileField(models.Model):
tmp_file = models.FileField(upload_to = './FileUpload/')# 上传目录为 FileUpload
file_user = models.ForeignKey(User, on_delete=models.CASCADE,null=True)
'''
值得注意的一点是,FileUpload中已经存在相同文件名的文件时,会对上传文件的文件名重命名
如 1.png 转为 1_fIZVhN3.png
且存储的文件为 1_fIZVhN3.png
'''
c_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.tmp_file.name
class Meta:
ordering = ["-c_time"]
verbose_name = "文件"
verbose_name_plural = "文件"
# 添加装饰器
@receiver(post_delete,sender=ModelWithFileField)
def delete_upload_files(sender, instance, **kwargs):
files = getattr(instance, 'tmp_file')
if not files:
return
fname = os.path.join(settings.MEDIA_ROOT, str(files))
if os.path.isfile(fname):
os.remove(fname)
views.py
def upload_file(request):
"""
上传文件
:param request:
:return:
"""
if not request.session.get('is_login', None): # 不允许重复登录
return redirect('/user/index/')
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
user_id = request.session.get('user_id')
if user_id:
tmp_user = models.User.objects.get(id=user_id)
instance = ModelWithFileField(tmp_file=request.FILES['file'], file_user=tmp_user)
instance.save()
message = "上传成功!\n存储的文件名为:\n" + instance.tmp_file.name
return render(request, 'job/upload.html', {'message_success': message})
else:
return render(request, 'job/upload.html', {'message_warning': "上传失败"})
else:
form = UploadFileForm()
return render(request, 'job/upload.html', {'form': form})
scan
scan设计思路如下:
- 利用
celery
和redis
,作为任务调度模块 - 当scan成功访问,
file
文件存在和无相关任务时,后台分别创建ScanTaskField
实例和启动celery
中的scanTask.delay(file_name=file_name)
- 当
celery
中任务完成,自动更新ScanTaskField
实例(同样需要添加装饰器)
参考文档docs.celeryproject.org/en/v5.0.5/django/first-steps-with-django
前端设计
前端设计上同样是基本参考于bootstrapdoc 5.0 example.
job/count
job/upload
Usage
- 下载
git clone https://github.com/fe1w0/ScanWebShell.git
cd ScanWebShell
- 配置环境
php vld
插件安装
http://pecl.php.net/package/vld
安装后php -m
来确定是否安装settings.py
cp ScanWebShell/settings.example.py ScanWebShell/settings.py
出于安全角度,SECRET_KEY
参数强烈建议修改,修改方法如下:
#进入Django shell
#python3 manage.py shell
#加载utils模块
from django.core.management import utils
#生成密钥
utils.get_random_secret_key()
邮箱(用于注册和重置密码功能)还需要在settings.py
中配置如下参数:
python3 -m pip install -r requirements.txt
python3 manage.py makemigrations
python3 manage.py migrate
python3 manage.py collectstatic
python3 manage.py createsuperuser
在celery
中设置worker
为redis
,需要
docker pull redis:latest
docker run --name=redis -d -p 6379:6379 redis
celery
启动
celery -A ScanWebShell worker -l info # 可以配合tmux或后台运行工具
- runserver
python3 manage.py runserver 0.0.0.0:8000
后续
比较有意思的是,当我在服务器上测试时,腾讯云也很快速地检测到了WebShell(复杂的WebShell没有进一步尝试)。。。厉害
感谢分享 赞一个