今天发现phpinfo_scanner的一个问题,当遇到证书不匹配等问题就爬不出来,然后我就加了个忽略证书错误。没啥大变动,只是备份一下
#-*- coding:utf-8 -*-import reimport requestsimport sysfrom bs4 import BeautifulSoup, elementfrom tabulate import tabulateimport wcwidthimport pprint#将重要信息取出,转化为dictdef info_to_dict(html):phpinfo_dict = {}#基本信息phpinfo_dict["Baseinfo"] = {}base_info_table = html.select("body > div > table:nth-child(2)")[0] if len(html.select("body > div > table:nth-child(2)")) != 0 else html.select("body > div > table:nth-child(3)")[0]for tr in base_info_table.find_all("tr"):key = tr.select("td.e")[0].string.strip()value = tr.select("td.v")[0].string.strip()phpinfo_dict["Baseinfo"][key] = valuefor h2 in html.find_all("h2"):module_name = h2.string.strip()phpinfo_dict[module_name] = {}#每一个配置模块是从h2标题开始的,向下寻找所有的table标签#有一个特殊情况PHP Credits,它在h1标签中,其内容是php及其sapi、module等的作者,对脚本功能没有意义,所以不解析for sibling in h2.next_siblings:#使用next_siblings会匹配到许多\n \t等,需特殊处理,官方文档明确提到if sibling.name != "table" and type(sibling) != element.NavigableString and sibling.name != "br":breakif sibling.name == "table":for tr in sibling.find_all("tr"):key_elements = tr.select("td.e")if len(key_elements) == 0:continuekey = key_elements[0].string.strip()value_elements = tr.select("td.v")if len(value_elements) == 0:value = ''elif len(value_elements) == 2:# 有些配置的value分为Local Value和Master Value# local value是当前目录的设置,会受.htaccess、.user.ini、代码中ini_set()等的影响# master value是php.ini中的值value = [value_elements[0].string.strip(), value_elements[1].string.strip()]else:value = "no value" if value_elements[0].string == None else value_elements[0].string.strip()phpinfo_dict[module_name][key] = value#windos _SERVER["xx"]#linux $_SERVER['xx']#消除这种差异php_var_dict = {}if list(phpinfo_dict["PHP Variables"].keys())[0][0] == "_":for key in phpinfo_dict["PHP Variables"].keys():new_key = "$" + key.replace('"', "'")php_var_dict[new_key] = phpinfo_dict["PHP Variables"][key]phpinfo_dict["PHP Variables"] = php_var_dictreturn phpinfo_dict#获取关键信息def get_important_info(phpinfo_dict):result = []#web目录result.append(["Web Path", phpinfo_dict["PHP Variables"]["$_SERVER['SCRIPT_FILENAME']"]])#真实ipserver_ip = phpinfo_dict["PHP Variables"]["$_SERVER['LOCAL_ADDR']"] if "$_SERVER['LOCAL_ADDR']" in phpinfo_dict["PHP Variables"] else phpinfo_dict["PHP Variables"]["$_SERVER['SERVER_ADDR']"]result.append(["Server IP", server_ip])#中间件result.append(["Software", phpinfo_dict["PHP Variables"]["$_SERVER['SERVER_SOFTWARE']"]])#php版本phpversion = phpinfo_dict["Core"]["PHP Version"]result.append(["PHP Version", phpversion])#系统信息result.append(["System", phpinfo_dict["Baseinfo"]["System"]])#SAPIresult.append(["Server API", phpinfo_dict["Baseinfo"]["Server API"]])#Registered PHP Streamsresult.append(["Registered PHP Streams", phpinfo_dict["Baseinfo"]["Registered PHP Streams"]])#allow_url_includeresult.append(["Allow Url Include", ", ".join(phpinfo_dict["Core"]["allow_url_include"])])#asp_tags#php7之后已移除if "asp_tags" in phpinfo_dict["Core"]:result.append(["Asp Tags", ", ".join(phpinfo_dict["Core"]["asp_tags"])])#short_open_tagresult.append(["Short Open Tag", ", ".join(phpinfo_dict["Core"]["short_open_tag"])])#enable_dlresult.append(["Enable Dl", ", ".join(phpinfo_dict["Core"]["enable_dl"])])#open_basedirresult.append(["Open Basedir", ", ".join(phpinfo_dict["Core"]["open_basedir"])])#sessionser_handler = ",".join(phpinfo_dict["session"]["session.serialize_handler"])upload_progress_enabled = ",".join(phpinfo_dict["session"]["session.upload_progress.enabled"])upload_progress_cleanup = ",".join(phpinfo_dict["session"]["session.upload_progress.cleanup"])upload_progress_name = ",".join(phpinfo_dict["session"]["session.upload_progress.name"])session_info = "session.serialize_handler: %s\nsession.upload_progress.enabled: %s\nsession.upload_progress.cleanup: %s\nsession.upload_progress.name: %s" % (ser_handler, upload_progress_enabled, upload_progress_cleanup, upload_progress_name)result.append(["Session", session_info])#curl#if "curl" in phpinfo_dict:#result.append(["Curl Protocols", phpinfo_dict["curl"]["Protocols"].replace(" ", "")])#libxmlif "libxml" in phpinfo_dict:result.append(["Libxml Version", phpinfo_dict["libxml"]["libXML Compiled Version"]])#disable functiondisable_func = phpinfo_dict["Core"]["disable_functions"][0]result.append(["Disable Function", disable_func.replace(",", "\n")])#extentionsextentions = ["imagick", "memcache", "redis", "xdebug", "opcache", "imap"]exist_ext = []for ext in extentions:if ext in phpinfo_dict:exist_ext.append(ext)intresting_exts = "No Intrestring Ext" if len(exist_ext) == 0 else ", ".join(exist_ext)result.append(["Extentions", intresting_exts])return tabulate(result, tablefmt="psql")#解析获取到的信息,如bypass_disable_function、php版本特性等def get_parsed_info(phpinfo_dict):result = []#php versionsuggestion = get_version_feature(phpinfo_dict["Core"]["PHP Version"])if suggestion:result.append([suggestion])#sapisapi = phpinfo_dict["Baseinfo"]["Server API"]if "FPM" in sapi:result.append(["SAPI为fpm,可能存在未授权访问漏洞"])#pharif "phar" in phpinfo_dict["Baseinfo"]["Registered PHP Streams"]:result.append(["支持phar协议,可扩展反序列化攻击面"])#ssrf curl php_wrapperprotocols = ["gopher", "dict"]available_protocols = []if "curl" in phpinfo_dict:for protocol in protocols:if protocol in phpinfo_dict["curl"]["Protocols"]:available_protocols.append(protocol)result.append(["libcurl支持%s协议" % (", ".join(available_protocols))])#libxml版本if "libxml" in phpinfo_dict and phpinfo_dict["libxml"]["libXML Compiled Version"] < "2.9":result.append(["libxml版本 < 2.9 xxe可利用"])#session upload progressif phpinfo_dict["session"]["session.upload_progress.enabled"][0] == "On":suggestion = "可利用session.upload_progress上传临时文件然后包含"if phpinfo_dict["session"]["session.upload_progress.cleanup"][0] == "On":suggestion += "\n临时文件会立刻删除,需用条件竞争getshell"result.append([suggestion])#session ser handlerif phpinfo_dict["session"]["session.serialize_handler"][0] != phpinfo_dict["session"]["session.serialize_handler"][1]:result.append(["ser handler不一致,存在反序列化风险"])#imagickif "imagick" in phpinfo_dict:result.append(["可利用imagick相关漏洞"])#xdebugif "xdebug" in phpinfo_dict and phpinfo_dict["xdebug"]["xdebug.remote_connect_back"][0] == "On" and phpinfo_dict["xdebug"]["xdebug.remote_enable"][0] == "On":result.append(["存在xdebug rce https://github.com/vulhub/vulhub/tree/master/php/xdebug-rce\nxdebug idekey: " + phpinfo_dict["xdebug"]["xdebug.idekey"][0]])#opcacheif "opcache" in phpinfo_dict:result.append(["可上传opcache覆盖源文件"])#imapif "imap" in phpinfo_dict:result.append(["可能存在imap rce https://github.com/vulhub/vulhub/blob/master/php/CVE-2018-19518/README.md"])#disable functionif phpinfo_dict["Core"]["disable_functions"][0] != "no value":result.append([bypass_disable_function(phpinfo_dict["Core"]["disable_functions"][0], phpinfo_dict)])return tabulate(result, tablefmt="grid")#根据版本获取版本特性def get_version_feature(version):suggestion = ""if "7.2" in version:suggestion = "php 7.2: assert从函数变为语法结构,无法动态调用; 移除create_function"if "7.0" in version:suggestion = "php 7.0: 移除dl; 不再支持asp_tag、<script language=\"php\">"return suggestion#如果存在disable_function,寻找可能的bypassdef bypass_disable_function(disable_func, phpinfo_dict):disable_func = disable_func.split(",")suggestion = ""bypass_func = []if "dl" not in disable_func and phpinfo_dict["Core"]["enable_dl"] == "On":bypass_func.append("dl")if "pcntl_exec" not in disable_func and "--enable-pcntl" in phpinfo_dict["Baseinfo"]["Configure Command"]:bypass_func.append("pcntl_exec")common_funcs = ['exec', 'system', 'passthru', 'popen', 'proc_open', 'shell_exec']for func in common_funcs:if func not in disable_func:bypass_func.append(func)suggestion += "可用函数:" + ", ".join(bypass_func) + "\n"if "Linux" in phpinfo_dict["Baseinfo"]["System"] and "putenv" not in disable_func and "mail" not in disable_func:suggestion += "使用LD_PRELOAD https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD\n"if "imap" in phpinfo_dict:suggestion += "使用imap https://github.com/vulhub/vulhub/blob/master/php/CVE-2018-19518/README.md\n"if "imagemagick" in phpinfo_dict:suggestion += "使用 ImageMagick\n"suggestion += "disable function bypass合集 https://github.com/l3m0n/Bypass_Disable_functions_Shell"return suggestiondef parse_phpinfo(url):r = requests.get(url,verify=False)#print(r.text)html = BeautifulSoup(r.text, "lxml")phpinfo_dict = info_to_dict(html)#pprint.pprint(phpinfo_dict)print(get_important_info(phpinfo_dict))print(get_parsed_info(phpinfo_dict))if __name__ == "__main__":url = sys.argv[1]parse_phpinfo(url)
requirements.txt文件内容
requests
bs4
tabulate
wcwidth
