Python实现Awvs自动化扫描
程序主要分三个功能模块,Url下载、批量扫描、结果入库,
有一个另外独立的程序,在流量镜像服务器上进行抓包,抓取Http协议的包,然后对包进行筛选处理,保留一些带有各种参数的url,然后对url进行入库。url下载功能主要是从mysql数据库中下载url。
批量扫描功能主要调用了Awvs的命令行wvs_console.exe,调研的时候先是尝试用awvs计划任务来实现批量扫描,但是发现在将一些awvs设置发包给计划任务的时候会遇到各种困难,计划任务貌似也不是一个网页,采用的是XML模版,技术有限,最后没实现,只好放弃。
之后发现Awvs支持命令行,wvs_console.exe就是主程序,有很多命令参数,基本能实现Awvs界面操作的所有功能。批量扫描主要是读取下载到本地的url,进行逐一扫描。要将扫描结果保存到本地access数据库文件,需要使用savetodatabase参数。
Awvs会自动将扫描结果保存到本地的access数据库中,具体的表是Wvs_alerts,也可以设置保存到Mssql数据库中,具体的是在Application Setting进行设置。结果入库模块的功能是从access数据库筛选出危害等级为3的漏洞,然后将它们写入到mysql数据库。主要用了正则表达式对request中的host,漏洞文件,get或post提交的请求进行筛选拼凑,获取到完整的漏洞测试url。
贴上代码,很菜,代码各种Bug,最主要的是程序的整个流程设计存在问题,导致后来被大佬给否掉了,没有考虑到重复扫描和程序异常中止的情况,导致我的程序只能一直运行下去 ,否则重新运行又会从头对url进行扫描。
对此很郁闷,某天晚上下班回家想了想,觉得可以通过以下方法来解决以上两个问题:
Awvs扫描结果数据库中有一个Wvs_scans表,保存的都是扫描过的url,以及扫描开始时间和结束时间。可以将当天下载的url保存到一个list中,然后在扫描之前先将之前所有扫描过的URL查询出来,同样保存在list中,读取list中的url,判断是否在扫描过的URL list中,如果存在将之从url list中删除掉;如果不存在则再进行扫描。
异常中止的话貌似只能增加系统计划任务,每天结束再打开,不知道如何时时监视系统进程,通过是否存在wvs的进程来判断。
以上只是一个大概的程序介绍,贴上代码,代码可能存在一些问题,有兴趣的童鞋去完善完善,大佬觉得我的效率和编码能力有待提高,so,让继续学习,所以没再继续跟进这个项目。
downurl.py 代码:
#coding:utf-8 import MySQLdb import os,time,shutil import hashlib,re datanow=time.strftime('%Y-%m-%d',time.localtime(time.time())) #filetype='.txt' #urlfilename=datanow+filetype #path="D:\wvscan\url\\" #newfile=path+urlfilename datanow=time.strftime('%Y-%m-%d',time.localtime(time.time())) con=MySQLdb.Connect('10.1.1.1','root','12345678','wvsdb') cur=con.cursor() sqlstr='select url from urls where time like %s' values=datanow+'%' cur.execute(sqlstr,values) data=cur.fetchall() #将当天的URL保存到本地url.txt文件 f=open(r'd:\Wvscan\url.txt','a+') uhfile=open(r'd:\Wvscan\urlhash.txt','a+') line=uhfile.readlines() #保存之前对url进行简单的处理,跟本地的hash文件比对,确保url.txt中url不重复 for i in range(0,len(data)): impurl=str(data[i][0]).split('=')[0] urlhash=hashlib.new('md5',impurl).hexdigest() urlhash=urlhash+'\n' if urlhash in line: pass else: uhfile.write(urlhash) newurl=str(data[i][0])+'\n' f.writelines(newurl) cur.close() con.close()
抓包程序抓到的url可能重复率较高,而且都是带参数的,保存到本地的时候截取了最前面的域名+目录+文件部分进行了简单的去重。Awvs可能不大适合这样的方式,只需要将全部的域名列出来,然后逐一扫描即可,site Crawler功能会自动爬行。
writeinmysql.py 结果入库模块,代码:
#coding:UTF-8 import subprocess import os,time,shutil,sys import win32com.client import MySQLdb import re,hashlib reload(sys) sys.setdefaultencoding('utf-8') #需要先在win服务器设置odbc源,指向access文件。实际用pyodbc模块可能更好一些 def writeinmysql(): conn = win32com.client.Dispatch(r'ADODB.Connection') DSN = 'PROVIDER=Microsoft Access Driver (*.mdb, *.accdb)' conn.Open('awvs') cur=conn.cursor() rs = win32com.client.Dispatch(r'ADODB.Recordset') rs.Open('[WVS_alerts]', conn, 1, 3) if rs.recordcount == 0: exit() #遍历所有的结果,cmp进行筛选危害等级为3的,也就是高危 while not rs.eof: severity = str(rs('severity')) if cmp('3', severity): rs.movenext continue vultype = rs('algroup') vulfile=rs('affects') #由于mysql库中要求的漏洞类型和access的名称有点差别,所以还需要对漏洞类型和危害等级进行二次命名,sql注入和xss为例 xss='Cross site' sqlinject='injection' if xss in str(vultype): vultype='XSS' level='低危' elif sqlinject in str(vultype): vultype="SQL注入" level='高危' else: level='中危' #拼凑出漏洞测试url,用了正则表达式, post和get类型的request请求是不同的 params = rs('parameter') ss = str(rs('request')) str1 = ss[0:4] if 'POST'== str1: requestType = 'POST' regex = 'POST (.*?) HTTP/1\.\d+' str1 = re.findall(regex, ss); else: requestType = 'GET' regex = 'GET (.*?) HTTP/1\.\d+' str1 = re.findall(regex, ss); regex = 'Host:(.*?)\r\n' host = re.findall(regex, ss); if host == []: host = '' else: host = host[0].strip() if str1 == []: str1 = '' else: str1 = str1[0] url =host + str1 timex=time.strftime('%Y-%m-%d',time.localtime(time.time())) status=0 scanner='Awvs' comment='' db = MySQLdb.connect(host="10.1.1.1", user="root", passwd="12345678", db="wvsdb",charset='utf8') cursor = db.cursor() sql = 'insert into vuls(status,comment,vultype,url,host,params,level,scanner) values(%s,%s,%s,%s,%s,%s,%s,%s)' values =[status,comment,vultype,'https://'+url.lstrip(),host,params,level,scanner] #入库的时候又进行了去重,确保mysql库中没有存在该条漏洞记录,跟本地保存的vulhash进行比对,感觉这种方法很原始。 hashvalue=str(values[2])+str(values[4])+str(vulfile)+str(values[5]) vulhash=hashlib.new('md5',hashvalue).hexdigest() vulhash=vulhash+'\n' file=open(r'D:\Wvscan\vulhash.txt','a+') if vulhash in file.readlines(): pass else: file.write(vulhash+'\n') cursor.execute(sql, values) delsql='delete from vuls where vultype like %s or vultype like %s' delvaluea='Slow HTTP%' delvalueb='Host header%' delinfo=[delvaluea,delvalueb] cursor.execute(delsql,delinfo) db.commit() rs.movenext rs.close conn.close cursor.close() db.close() if __name_=='__main__': writeinmysql() time.sleep(10) #备份每天的扫描数据库,用原始数据库替换,方便第二天继续保存。 datanow=time.strftime('%Y-%m-%d',time.localtime(time.time())) filetype='.mdb' urlfilename=datanow+filetype path="D:\wvscan\databak\\" databakfile=path+urlfilename shutil.copyfile(r'D:\Wvscan\data\vulnscanresults.mdb',databakfile) shutil.copyfile(r'D:\Wvscan\vulnscanresults.mdb',r'D:\Wvscan\data\vulnscanresults.mdb')
startwvs.py,扫描模块,这个模块问题较多,就是之前提到的,没有考虑重复扫描和异常终止的问题,贴上代码:
#coding:utf-8 import subprocess import os,time,shutil file=open(r"D:\Wvscan\url.txt","r") def wvsscan(): for readline in file: url=readline.strip('\n') cmd=r"d:\Wvs\wvs_console.exe /Scan "+url+r" /SavetoDatabase" doscan=subprocess.Popen(cmd) doscan.wait() if __name__=='__main__': wvsscan() 阿德马原创,转载请注明出处,Tks!