| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| import re |
| import os |
| |
| from projectModel import totle_db |
| class wordpress_scan: |
|
|
| |
| plugin_default_headers = { |
| "Name": "Plugin Name", |
| "PluginURI": "Plugin URI", |
| "Version": "Version", |
| "Description": "Description", |
| "Author": "Author", |
| "AuthorURI": "Author URI", |
| "TextDomain": "Text Domain", |
| "DomainPath": "Domain Path", |
| "Network": "Network", |
| "RequiresWP": "Requires at least", |
| "RequiresPHP": "Requires PHP", |
| "UpdateURI": "Update URI", |
| "RequiresPlugins": "Requires Plugins", |
| "_sitewide": "Site Wide Only" |
| } |
|
|
| |
| theme_default_headers = { |
| "Name": "Theme Name", |
| "Title": "Theme Name", |
| "Version": "Version", |
| "Author": "Author", |
| "AuthorURI": "Author URI", |
| "UpdateURI": "Update URI", |
| "Template": "Theme Name", |
| "Stylesheet": "Theme Name", |
| } |
|
|
| def M(self, table): |
| ''' |
| @name 获取数据库对象 |
| @param table 表名 |
| @param db 数据库名 |
| ''' |
| with totle_db.Sql().dbfile("../wordpress") as sql: |
| return sql.table(table) |
|
|
| def get_plugin_data(self, plugin_file, default_headers, context=''): |
| ''' |
| @参考:/wp-admin/includes/plugin.php get_plugin_data 代码 |
| @name 获取插件信息 |
| @param plugin_file 插件文件 |
| @return dict |
| @auther lkq |
| @time 2024-10-08 |
| ''' |
| |
| if not os.path.exists(plugin_file): return {} |
| |
| max_length = 8 * 1024 |
| try: |
| |
| with open(plugin_file, 'r', encoding='utf-8') as file: |
| file_data = file.read(max_length) |
| except Exception as e: |
| return {} |
| |
| file_data = file_data.replace('\r', '\n') |
| |
| extra_headers = {} |
| if context: |
| extra_context_headers = [] |
| |
| |
| extra_headers = dict.fromkeys(extra_context_headers, '') |
| all_headers = {**extra_headers, **default_headers} |
|
|
| |
| for field, regex in all_headers.items(): |
| if field.startswith('_'): |
| continue |
| match = re.search(f'{regex}:(.*)$', file_data, re.IGNORECASE | re.MULTILINE) |
| if match: |
| all_headers[field] = match.group(1).strip() |
| else: |
| all_headers[field] = '' |
| if all_headers.get("Network") and not all_headers['Network'] and all_headers['_sitewide']: |
| all_headers['Network'] = all_headers['_sitewide'] |
| if all_headers.get("Network"): |
| all_headers['Network'] = 'true' == all_headers['Network'].lower() |
| if all_headers.get("_sitewide"): |
| del all_headers['_sitewide'] |
|
|
| if all_headers.get("TextDomain") and not all_headers['TextDomain']: |
| plugin_slug = os.path.dirname(os.path.basename(plugin_file)) |
| if '.' != plugin_slug and '/' not in plugin_slug: |
| all_headers['TextDomain'] = plugin_slug |
|
|
| all_headers['Title'] = all_headers['Name'] |
| all_headers['AuthorName'] = all_headers['Author'] |
|
|
| |
| return all_headers |
|
|
| def Md5(self,strings): |
| """ |
| @name 生成MD5 |
| @author hwliang<hwl@bt.cn> |
| @param strings 要被处理的字符串 |
| @return string(32) |
| """ |
| if type(strings) != bytes: |
| strings = strings.encode() |
| import hashlib |
| m = hashlib.md5() |
| m.update(strings) |
| return m.hexdigest() |
|
|
| def FileMd5(self,filename): |
| """ |
| @name 生成文件的MD5 |
| @author hwliang<hwl@bt.cn> |
| @param filename 文件名 |
| @return string(32) or False |
| """ |
| if not os.path.isfile(filename): return False |
| import hashlib |
| my_hash = hashlib.md5() |
| f = open(filename, 'rb') |
| while True: |
| b = f.read(8096) |
| if not b: |
| break |
| my_hash.update(b) |
| f.close() |
| return my_hash.hexdigest() |
| def get_plugin(self, path,one=''): |
| ''' |
| @name 获取WordPress插件信息 |
| @param path 插件路径 |
| @return dict |
| @auther lkq |
| @time 2024-10-08 |
| ''' |
| plugin_path = path + "/wp-content/plugins" |
| if not os.path.exists(plugin_path): return {} |
| tmp_list = [] |
| for file in os.listdir(plugin_path): |
| if one: |
| if file!=one:continue |
| plugin_file = os.path.join(plugin_path, file) |
| |
| |
| if os.path.isdir(plugin_file): |
| |
| for file2 in os.listdir(plugin_file): |
| plugin_file2 = os.path.join(plugin_file, file2) |
| if os.path.isfile(plugin_file2) and plugin_file2.endswith(".php"): tmp_list.append( |
| file + "/" + file2) |
| if len(tmp_list) == 0: return {} |
| result = {} |
|
|
| for i in tmp_list: |
| plugin_file = plugin_path + "/" + i |
| |
| if not os.access(plugin_file, os.R_OK): continue |
| plugin_data = self.get_plugin_data(plugin_file, self.plugin_default_headers) |
| if not plugin_data: continue |
| if plugin_data["Name"] == "": continue |
| |
| if "/" not in i: |
| |
| if 'wordpress.org/plugins/' in plugin_data["PluginURI"]: |
| plugin_data["PluginURI"] = plugin_data["PluginURI"].replace('http://wordpress.org/plugins/', '').replace("http://wordpress.org/plugins/","") |
| |
| if plugin_data["PluginURI"][-1]=="/": |
| plugin_data["PluginURI"]=plugin_data["PluginURI"][:-1] |
| i=plugin_data["PluginURI"] |
| else: |
| continue |
| result[i] = plugin_data |
| return result |
|
|
|
|
| def compare_versions(self,version1, version2): |
| ''' |
| @name 对比版本号 |
| @param version1 版本1 |
| @param version2 版本2 |
| @return int 0 相等 1 大于 -1 小于 |
| ''' |
| |
| v1 = [int(num) for num in version1.split('.')] |
| v2 = [int(num) for num in version2.split('.')] |
| |
| for num1, num2 in zip(v1, v2): |
| if num1 > num2: |
| return 1 |
| elif num1 < num2: |
| return -1 |
| |
| if len(v1) > len(v2): |
| return 1 if any(num > 0 for num in v1[len(v2):]) else 0 |
| elif len(v1) < len(v2): |
| return -1 if any(num > 0 for num in v2[len(v1):]) else 0 |
| |
| return 0 |
| def let_identify(self,version,vlun_infos): |
| ''' |
| @name 对比版本号判断是否存在漏洞 |
| @param version 当前版本 |
| @param vlun_infos 漏洞信息 |
| @return list |
| ''' |
| for i in vlun_infos: |
| i["vlun_status"] = False |
| |
| if i["let"]=="<=": |
| if self.compare_versions(version,i["vlun_version"])<=0: |
| i["vlun_status"]=True |
| |
| if i["let"]=="<": |
| if self.compare_versions(version,i["vlun_version"])<0: |
| i["vlun_status"]=True |
| if i['let']=='-': |
| |
| version_list=i["vlun_version"].split("-") |
| if len(version_list)!=2:continue |
| if self.compare_versions(version,version_list[0])>=0 and self.compare_versions(version,version_list[1])<=0: |
| i["vlun_status"]=True |
|
|
| return vlun_infos |
|
|
| def scan(self,path): |
| ''' |
| @name 扫描WordPress |
| @param path WordPress路径 |
| @return dict |
| @auther lkq |
| @time 2024-10-10 |
| @msg 通过扫描WordPress的版本、插件、主题来判断是否存在漏洞 |
| ''' |
| vlun_list = [] |
| |
| import os |
| if not os.path.exists(path): |
| return vlun_list |
| result = {} |
| result["plugins"] = self.get_plugin(path) |
| |
| for i in result["plugins"]: |
| plguin=i.split("/")[0] |
| Name=result["plugins"][i]["Name"] |
| if result["plugins"][i]["Version"]=="":continue |
| |
| if self.M("vulnerabilities").where("plugin=?",(plguin,)).count()>0: |
| vlun_infos=self.M("vulnerabilities").where("plugin=?",(plguin)).select() |
| vlun_infos=self.let_identify(result["plugins"][i]["Version"],vlun_infos) |
| for j2 in vlun_infos: |
| if j2["vlun_status"]: |
| vlun = {"name": "", "vlun_info": "", "css": "", "type": "plugin", "load_version": "","cve": "","time":""} |
| vlun["load_version"]=result["plugins"][i]["Version"] |
| vlun["cve"]=j2["cve"] |
| vlun["slug"]=plguin |
| vlun["name"] = Name |
| vlun["vlun_info"]=j2["msg"] |
| vlun["css"]=j2["css"] |
| vlun["time"] = j2["time"] |
| vlun_list.append(vlun) |
|
|
| return vlun_list |