0

0

WindowsVulnScan | 一款基于主机的漏洞扫描工具

看不見的法師

看不見的法師

发布时间:2025-09-02 08:12:01

|

940人浏览过

|

来源于php中文网

原创

0x00 说明:

这是一款基于主机的漏洞扫描工具,采用多线程确保可以快速的请求数据,采用线程锁可以在向sqlite数据库中写入数据避免

database is locked
的错误,采用
md5
哈希算法确保数据不重复插入。

本工具查找是否有公开exp的网站为

shodan
,该网站限制网络发包的速度,因而采用了单线程的方式,且耗时较长。

功能:

查找主机上具有的CVE查找具有公开EXP的CVE
WindowsVulnScan | 一款基于主机的漏洞扫描工具
0x01 起因:

因为需要做一些主机漏洞扫描方面的工作,因而编写了这个简单的工具。之前也查找了几款类似的工具,如下:

vulmap
:

windows-exp-suggester
:

基于以上所以写了这个简单的工具,该项目在https://github.com/chroblert/WindowsVulnScan

0x02 原理:

1. 搜集CVE与KB的对应关系。首先在微软官网上收集CVE与KB对应的关系,然后存储进数据库中

2. 查找特定CVE网上是否有公开的EXP

3. 利用powershell脚本收集主机的一些系统版本与KB信息

北极象沉浸式AI翻译
北极象沉浸式AI翻译

免费的北极象沉浸式AI翻译 - 带您走进沉浸式AI的双语对照体验

下载

4. 利用系统版本与KB信息搜寻主机上具有存在公开EXP的CVE

0x03 参数:代码语言:javascript代码运行次数:0运行复制
# author: JC0o0l# GitHub: https://github.com/chroblert/可选参数:  -h, --help            show this help message and exit  -u, --update-cve      更新CVEKB数据  -U, --update-exp      更新CVEEXP数据  -C, --check-EXP       检索具有EXP的CVE  -f FILE, --file FILE  ps1脚本运行后产生的.json文件
0x04 示例:

1. 首先运行powershell脚本

KBCollect.ps
收集一些信息

代码语言:javascript代码运行次数:0运行复制
.\KBCollect.ps1

2. 将运行后产生的

KB.json
文件移动到
cve-check.py
所在的目录

3. 安装一些python3模块

代码语言:javascript代码运行次数:0运行复制
python3 -m pip install requirements.txt

4. 运行

cve-check.py -u
创建CVEKB数据库

5. 运行

cve-check.py -u
更新CVEKB数据库中的
hasPOC
字段

6. 运行

cve-check.py -C -f KB.json
查看具有公开EXP的CVE,如下:

WindowsVulnScan | 一款基于主机的漏洞扫描工具
0x05 源码:

KBCollect.ps1
:

代码语言:javascript代码运行次数:0运行复制
function Get-CollectKB(){    # 1. 搜集所有的KB补丁    $KBArray = @()    $KBArray = Get-HotFix|ForEach-Object {$_.HotFixId}    $test = $KBArray|ConvertTo-Json    return $test}function Get-BasicInfo(){    # 1. 操作系统    # $windowsProductName = (Get-ComputerInfo).WindowsProductName    $windowsProductName = (Get-CimInstance Win32_OperatingSystem).Caption    # 2. 操作系统版本    $windowsVersion = (Get-ComputerInfo).WindowsVersion    $basicInfo = "{""windowsProductName"":""$windowsProductName"",""windowsVersion"":""$windowsVersion""}"    return $basicInfo}$basicInfo = Get-BasicInfo$KBList = Get-CollectKB$KBResult = "{""basicInfo"":$basicInfo,""KBList"":$KBList}"$KBResult|Out-File KB.json -encoding utf8

cve-check.py
:

代码语言:javascript代码运行次数:0运行复制
import requestsimport sqlite3import jsonimport hashlibimport mathimport reimport threadingimport timeimport argparsefrom pathlib import Path# 删除一些ssl 认证的warnging信息requests.packages.urllib3.disable_warnings()ThreadCount=20DBFileName="CVEKB.db"TableName="CVEKB"insertSQL = []updateSQL = []lock = threading.Lock()KBResult = {}parser = argparse.ArgumentParser()parser.add_argument("-u","--update-cve",help="更新CVEKB数据",action="store_true")parser.add_argument("-U","--update-exp",help="更新CVEEXP数据",action="store_true")parser.add_argument("-C","--check-EXP",help="检索具有EXP的CVE",action="store_true")parser.add_argument("-f","--file",help="ps1脚本运行后产生的.json文件")args = parser.parse_args()class CVEScanThread(threading.Thread):    def __init__(self,func,args,name="",):        threading.Thread.__init__(self)        self.func = func        self.args = args        self.name = name         self.result = None    def run(self):        print("thread:{} :start scan page {}".format(self.args[1],self.args[0]))        self.result = self.func(self.args[0],)        print("thread:{} :stop scan page {}".format(self.args[1],self.args[0]))class EXPScanThread(threading.Thread):    def __init__(self,func,args,name="",):        threading.Thread.__init__(self)        self.func = func        self.args = args        self.name = name         self.result = None    def run(self):        print("thread:{} :start scan CVE: {},xuehao:{}".format(self.args[1],self.args[0],self.args[2]))        self.result = self.func(self.args[0],)        print("thread:{} :stop scan CVE: {}".format(self.args[1],self.args[0]))    def get_result(self):        threading.Thread.join(self)        try:            return self.result        except Exception:            return "Error"def get_page_num(num=1,pageSize=100):    url = "https://portal.msrc.microsoft.com/api/security-guidance/en-us"    payload = "{\"familyIds\":[],\"productIds\":[],\"severityIds\":[],\"impactIds\":[],\"pageNumber\":" + str(num) + ",\"pageSize\":" + str(pageSize) + ",\"includeCveNumber\":true,\"includeSeverity\":false,\"includeImpact\":false,\"orderBy\":\"publishedDate\",\"orderByMonthly\":\"releaseDate\",\"isDescending\":true,\"isDescendingMonthly\":true,\"queryText\":\"\",\"isSearch\":false,\"filterText\":\"\",\"fromPublishedDate\":\"01/01/1998\",\"toPublishedDate\":\"03/02/2020\"}"    headers = {        'origin': "https//portal.msrc.microsoft.com",        'referer': "https//portal.msrc.microsoft.com/en-us/security-guidance",        'accept-language': "zh-CN",        'user-agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299",        'accept': "application/json, text/plain, */*",        'accept-encoding': "gzip, deflate",        'host': "portal.msrc.microsoft.com",        'connection': "close",        'cache-control': "no-cache",        'content-type': "application/json",        }    response = requests.request("POST", url, data=payload, headers=headers, verify = False)    resultCount = json.loads(response.text)['count']    return math.ceil(int(resultCount)/100)def update_cvekb_database(num=1,pageSize=100):    pageCount = get_page_num()    #for i in range(1,pageCount+1):    i = 1    pageCount=524    tmpCount = ThreadCount    while i <= pageCount:        tmpCount = ThreadCount if (pageCount - i) >= ThreadCount else pageCount - i        print("i:{},pageCount-i:{},ThreadCount:{},PageCount:{}".format(i,pageCount-i,ThreadCount,pageCount))        time.sleep(0.5)            threads = []        print("===============================")        for j in range(1,tmpCount + 1):            print("更新第{}页".format(i+j-1))            t = CVEScanThread(update_onepage_cvedb_database,(i+j-1,j,),str(j))            threads.append(t)        for t in threads:            t.start()        for t in threads:            t.join()            # update_onepage_cvedb_database(num=i)        i = i + tmpCount        conn = sqlite3.connect(DBFileName)        for sql in insertSQL:            conn.execute(sql)        conn.commit()        conn.close()        if tmpCount != ThreadCount:            breakdef check_POC_every_CVE(CVEName=""):    #apiKey = ""    #url = "https://exploits.shodan.io/api/search?query=" + CVEName + "&key=" + apiKey    url = "https://exploits.shodan.io/?q=" + CVEName    try:        response = requests.request("GET",url=url,verify=False,timeout=10)        #total = json.loads(response.text)    except Exception as e:        print("Error,{}".format(CVEName))        print(e)        return "Error"    if "Total Results" not in response.text:        return "False"    else:        return "True"def update_onepage_cvedb_database(num=1,pageSize=100):    url = "https://portal.msrc.microsoft.com/api/security-guidance/en-us"    payload = "{\"familyIds\":[],\"productIds\":[],\"severityIds\":[],\"impactIds\":[],\"pageNumber\":" + str(num) + ",\"pageSize\":" + str(pageSize) + ",\"includeCveNumber\":true,\"includeSeverity\":false,\"includeImpact\":false,\"orderBy\":\"publishedDate\",\"orderByMonthly\":\"releaseDate\",\"isDescending\":true,\"isDescendingMonthly\":true,\"queryText\":\"\",\"isSearch\":false,\"filterText\":\"\",\"fromPublishedDate\":\"01/01/1998\",\"toPublishedDate\":\"03/02/2020\"}"    headers = {        'origin': "https//portal.msrc.microsoft.com",        'referer': "https//portal.msrc.microsoft.com/en-us/security-guidance",        'accept-language': "zh-CN",        'user-agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299",        'accept': "application/json, text/plain, */*",        'accept-encoding': "gzip, deflate",        'host': "portal.msrc.microsoft.com",        'connection': "close",        'cache-control': "no-cache",        'content-type': "application/json",        }    try:        response = requests.request("POST", url, data=payload, headers=headers, verify = False)        resultList = json.loads(response.text)['details']    except :        print(response.text)    conn = sqlite3.connect(DBFileName)    create_sql = """Create Table IF NOT EXISTS {} (        hash TEXT UNIQUE,        name TEXT,        KBName TEXT,        CVEName TEXT,        impact TEXT,        hasPOC TEXT)""".format(TableName)    conn.execute(create_sql)    conn.commit()    conn.close()    for result in resultList:        KBName = result['articleTitle1'] + ";" if (result['articleTitle1'] !=  None) and result['articleTitle1'].isdigit() else ""        KBName += result['articleTitle2'] + ";" if (result['articleTitle2'] != None) and result['articleTitle2'].isdigit() else ""        KBName += result['articleTitle3'] + ";" if (result['articleTitle3'] != None) and result['articleTitle3'].isdigit() else ""        KBName += result['articleTitle4'] + ";" if (result['articleTitle4'] != None) and result['articleTitle4'].isdigit() else ""        if KBName == "":            continue        h1 = hashlib.md5()        metaStr = result['name'] + KBName + result['cveNumber'] + result['impact']        h1.update(metaStr.encode('utf-8'))        #hasPOC = check_POC_every_CVE(result['cveNumber'])        # 收集到所有的KB后再搜索有没有公开的EXP        hasPOC = ""        sql = "INSERT OR IGNORE INTO "+TableName+" VALUES ('" + h1.hexdigest() + "','" + result['name'] + "','" + KBName + "','" + result['cveNumber'] + "','" + result['impact'] + "','" + hasPOC+"')"        with lock:            global insertSQL            insertSQL.append(sql)        # conn.execute(sql)    # conn.commit()    # conn.close()    # passdef select_CVE(tmpList=[],windowsProductName="",windowsVersion=""):    conn = sqlite3.connect(DBFileName)    con = conn.cursor()    intersectionList = []    count = 0    for i in tmpList:        sql = 'select distinct(CVEName) from '+ TableName+' where (name like "'+ windowsProductName+'%'+ windowsVersion + '%") and ("'+ i +'" not in (select KBName from '+ TableName +' where name like "'+ windowsProductName+'%'+windowsVersion+'%")); '        cveList = []        for cve in con.execute(sql):            cveList.append(cve[0])        if count == 0:            intersectionList = cveList.copy()        count +=1        intersectionList = list(set(intersectionList).intersection(set(cveList)))    intersectionList.sort()    for cve in intersectionList:        sql = "select CVEName from {} where CVEName == '{}' and hasPOC == 'True'".format(TableName,cve)        # print(sql)        con.execute(sql)        if len(con.fetchall()) != 0:            print("{} has EXP".format(cve))    # print(intersectionList)def update_hasPOC(key = "Empty"):    conn = sqlite3.connect(DBFileName)    con = conn.cursor()    if key == "All":        sql = "select distinct(CVEName) from {}".format(TableName)    else:        sql = "select distinct(CVEName) from {} where (hasPOC IS NULL) OR (hasPOC == '')".format(TableName)    con.execute(sql)    cveNameList = con.fetchall()    i = 0    count = 1    while i < len(cveNameList):        print("|=========={}============|".format(i))        # tmpCount = ThreadCount if (len(cveNameList) - i) >= ThreadCount else len(cveNameList) - i        # threads = []        # for j in range(1,tmpCount+1):        #     t = EXPScanThread(check_POC_every_CVE,(cveNameList[i+j][0],j,i+j,),str(j))        #     threads.append(t)        # for t in threads:        #     t.start()        # for t in threads:        #     t.join()        # j = 1        # for t in threads:        #     hasPOC = t.get_result()        #     print(hasPOC)        #     update_sql = "UPDATE "+TableName+" set hasPOC = '" + hasPOC + "' WHERE cveName == '" + cveNameList[i+j][0] +"';"        #     conn.execute(update_sql)        #     print("[+] update:{}".format(update_sql))        #     j += 1        # i=i+ThreadCount        # conn.commit()        hasPOC = check_POC_every_CVE(cveNameList[i][0])        time.sleep(0.3)        update_sql = "UPDATE "+TableName+" set hasPOC = '" + hasPOC + "' WHERE cveName == '" + cveNameList[i][0] +"';"        conn.execute(update_sql)        print(update_sql)        count += 1        i += 1        if count == 10:            conn.commit()            print("[+]update")            count = 1    conn.commit()    conn.close()    print("Over")if __name__ == "__main__":    banner = """    ========CVE-EXP-Check===============    |       author:JC0o0l               |    |       wechat:JC_SecNotes          |    |       version:1.0                 |    =====================================    """    print(banner)    if (not args.check_EXP ) and (not args.update_cve) and (not args.update_exp) and args.file is None:        parser.print_help()    if args.update_cve:        update_cvekb_database()    if args.update_exp:        dbfile=Path(DBFileName)        if dbfile.exists():            update_hasPOC(key="Empty")        else:            print("请先使用-u 创建数据库")            parser.print_help()    if args.check_EXP:        dbfile=Path(DBFileName)        if not dbfile.exists():            print("请先使用-u 创建数据库,之后使用 -U 更新是否有EXP")            parser.print_help()            exit()        if args.file:            with open(args.file,"r",encoding="utf-8") as f:                KBResult = json.load(f)            windowsProductName = KBResult['basicInfo']['windowsProductName']            windowsProductName = ((re.search("\w[\w|\s]+\d+[\s|$]",windowsProductName).group()).strip()).replace("Microsoft","").strip()            windowsVersion = KBResult['basicInfo']['windowsVersion']            print("系统信息如下:")            print("{} {}".format(windowsProductName,windowsVersion))            tmpKBList = KBResult['KBList']            KBList = []            for KB in tmpKBList:                KBList.append(KB.replace("KB",""))            print("KB信息如下:")            print(KBList)            print("EXP信息如下:")            select_CVE(tmpList=KBList,windowsProductName=windowsProductName,windowsVersion=windowsVersion)        else:            print("请输入.json文件")

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

556

2023.06.20

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

374

2023.07.04

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

732

2023.07.04

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

477

2023.09.01

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

414

2023.09.04

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

991

2023.09.04

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

658

2023.09.12

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

553

2023.09.20

PHP WebSocket 实时通信开发
PHP WebSocket 实时通信开发

本专题系统讲解 PHP 在实时通信与长连接场景中的应用实践,涵盖 WebSocket 协议原理、服务端连接管理、消息推送机制、心跳检测、断线重连以及与前端的实时交互实现。通过聊天系统、实时通知等案例,帮助开发者掌握 使用 PHP 构建实时通信与推送服务的完整开发流程,适用于即时消息与高互动性应用场景。

3

2026.01.19

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 4.7万人学习

Django 教程
Django 教程

共28课时 | 3.2万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号