0x01 需求
openssl漏洞,struts2漏洞,这些都是一些爆发型的事件漏洞。在复查自身是否存在问题,或者说受影响面有多大的时候,谁有好的自动化工具,谁就最快解决问题。当然,针对特殊的人群,定制化的py脚本也能解决问题,只是相对来说,这个过程会比较麻烦,假如,正好你今天请假,没有上班,且目前只有一个实习生在工作岗位上,处理这个问题,可能瞬间他就懵了。这个时候,我们就要考虑有一个完整的一体化的安全自动化插件检测框架。(例如: tangscan之类的)
0x02 开发
最早参考的是sqlmap的开发模型进行研究,因为sqlmap做得很强大,起码我是这么认为的。so,同时想用一些新的东西提高下自己的能力。
先说下整个扫描器应该分为以下两块:
- web api 模块
- 执行模块
从api上,应该跟sqlmap一样,接受任务、执行任务、返回结果之类的。当然,我也是这样设计的。
截取部分代码,作为样例:
# 默认方法
@route('/',method='GET')
def index():
if reqIp(request.environ.get('REMOTE_ADDR')):
return '欢迎访问SPScanner V3.0\n'
else:
return json.dumps({'status':-5})
# 添加任务方法
@route('/scan/add',method='POST')
def addtask(_ip='',_web=''):
try:
if reqIp(request.environ.get('REMOTE_ADDR')):
_type = int(request.forms.get('type'))
_ip = request.forms.get('ip')
_web = request.forms.get('web')
task_id = createHashID()
insert_task_sql = 'INSERT INTO task(ID,TYPE,TASKVAL,TASKSTATUS,RESPORTID,TASKID)VALUES(null,?,?,?,?,?);'
if _type == 1:
for ip in _ip.split(','):
report_id = createHashID()
dbconn.execute(insert_task_sql,(1,ip,0,report_id,task_id));
dbconn.commit()
return json.dumps({'status':1,'taskid':task_id})
elif _type == 2:
for url in _web.split(','):
report_id = createHashID()
dbconn.execute(insert_task_sql,(2,url,0,report_id,task_id));
dbconn.commit()
return json.dumps({'status':1,'taskid':task_id})
else:
return json.dumps({'status':0})
else:
return json.dumps({'status':-5})
except Exception, e:
return json.dumps({'status':-1})
# 删除任务方法
@route('/scan/del',method='POST')
def deltask():
try:
if reqIp(request.environ.get('REMOTE_ADDR')):
_taskid = request.forms.get('taskid')
select_task_sql = "SELECT * FROM task where TASKID = '?';"
data = dbconn.execute(select_task_sql,(_taskid))
if len(data) > 0:
delete_task_sql = "DELETE FROM task WHERE TASKID = '?';"
dbconn.execute(delete_task_sql,(_taskid))
dbconn.commit()
return json.dumps({'status':1})
else:
return json.dumps({'status':0})
else:
return json.dumps({'status':-5})
except Exception, e:
return json.dumps({'status':-1})
# 获得任务状态方法
# 返回任务完成的百分比
@route('/scan/status',method='POST')
def getstatus():
try:
_taskid = request.forms.get('taskid')
select_task_sql = "SELECT * FROM task WHERE TASKID = '%s';" % _taskid
alldata = dbconn.execute(select_task_sql)
if len(alldata) > 0:
all_count = len(alldata)
finish_task_sql = "SELECT * FROM task WHERE TASKSTATUS = '1' AND TASKID = '%s';" % _taskid
finish_count = dbconn.execute(finish_task_sql)
penten = int(float(len(finish_count)/len(alldata))*100)
return json.dumps({'status':1,'penten':penten})
else:
return json.dumps({'status':0})
except Exception, e:
return json.dumps({'status':-1})
# 读取任务报告
@route('/scan/report',method='POST')
def getreport():
try:
reportdict = {}
_taskid = request.forms.get('taskid')
select_task_sql = "SELECT RESPORTID FROM task WHERE TASKID = '%s';" % _taskid
alldata = dbconn.execute(select_task_sql)
if len(alldata) > 0:
for taskitem in alldata:
reportid = taskitem[0]
select_report_sql = "SELECT * FROM report WHERE RESPORTID = '%s';" % reportid
reportdata = dbconn.execute(select_report_sql)
for reportitem in reportdata:
reportdict[reportitem[0]] = {
'vulname': reportitem[1],
'vullevel': reportitem[2],
'vulref': reportitem[3],
'vulsdesc': reportitem[4],
'vulddesc': reportitem[5],
'vulrespheader': reportitem[6],
'vulresqheader': reportitem[7],
'vulresquest': reportitem[8]
}
reportdict['status'] = 1
return json.dumps(reportdict)
else:
return json.dumps({'status':0})
except Exception, e:
return json.dumps({'status':-1})
在数据库这块,默认我选择sqlite,因为我觉得用这个做为本地的agent足够了,在后面读取了报告之后,把结果存到redis/MYSQL,其实才是最靠谱的做法。当然,你需要把这个东西做成守护进程执行~
0x03 目录结构
目录结构基本上比较清晰,命名规范都比较标准,之前犯过的一些错误都会尽可能的避免,所以这个版本基本上都是按照标准化来coding,目的在于长久使用和维护。
├── TaskService.py ==> (任务处理service)
├── WebApiService.py ==> (web服务service)
├── conf ==> (配置)
│ ├── Global_Conf.py
│ ├── Http_Request_Conf.py
│ ├── Plugin_Path_Conf.py
├── daemon ==> (守护进程)
│ ├── pscanner_taskservice
│ └── pscanner_webapiservice
├── lib ==> (基础库)
│ ├── common
│ │ ├── Http_Request.py
│ │ ├── coredata.py
│ │ ├── execute.py
│ │ ├── general.py
│ │ └── special.py
│ ├── core
│ │ ├── Scan_Exploit.py
│ │ ├── Scan_Frame.py
│ │ ├── Scan_Print.py
│ └── objects
│ └── task.py
├── log ==> (日志管理)
│ ├── access
│ ├── error
│ └── info
├── plugins ==> (插件库)
│ ├── system
│ │ ├── M_4cc4d9c6fed65e0dc01adcebbf2f2158.py
│ └── website
│ ├── E_514f056a3ff58eb000f1a94fae988b7b.py
│ ├── E_e8773f08f8f10c34dec6ae699ca1b111.py
│ ├── G_29e34bc8c04565ec9b88c26cdb5ebbc8.py
│ ├── O_2e7e01af7a74ccaee996632665d6a6af.py
├── run.py ==> (扫描器console版本入口)
└── setup.py ==> (环境安装脚本)
0x04 关于 Web Service API:
该api主要用于分布式使用,调度器通过发起http请求将任务下发执行,在扫描结束后将任务报告结果提取。
请求类型 | 请求地址 | 请求参数 | 作用说明 |
---|---|---|---|
GET | / | 无请求值 | 默认页面 |
POST | /scan/add | 扫描ip:ip,扫描web:web,扫描类型:type | 创建一个扫描任务 |
POST | /scan/del | 任务id:taskid | 删除扫描任务信息 |
POST | /scan/status | 任务id:taskid | 读取扫描任务状态 |
POST | /scan/report | 任务id:taskid | 读取扫描任务报告 |
0x05 next
下一章讲核心功能设计。。。