安全矩阵

 找回密码
 立即注册
搜索
查看: 1125|回复: 0

Nuclei Fuzzer 实战指南:自动化 Web 应用安全测试的优化与实践

[复制链接]

417

主题

417

帖子

2391

积分

金牌会员

Rank: 6Rank: 6

积分
2391
发表于 2023-12-7 21:30:41 | 显示全部楼层 |阅读模式
零乱 随风安全 2023-12-04 13:00 发表于北京

声明

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。
目录
前言
项目研究
    fuzzing-templates
    ParamSpider
代码实现
    总体设计
    爬虫函数
    工具函数
        路径问题
        爬虫结果问题
        HTTP协议头问题
        随机UA头
        保存路径
    扫描函数
    主函数
    小彩蛋
总结
继续一个彩蛋
前言
一次网上冲浪的过程中发现了一个项目https://github.com/0xKayala/NucleiFuzzer,抱着看看的心态开始了一段不归路,从有想法,到最后的勉强实现,花费了我整整一天的时间,期间十分的痛苦(现在要写这篇文章就更痛苦了)。整篇文章的思路,我会按照我自己实现的整个流程来,期间可能会有点乱,希望大家理解。
项目研究
NucleiFuzzer 是一款自动化工具,结合了ParamSpider和Nuclei ,以增强Web应用程序安全测试。它使用ParamSpider来识别潜在的入口点,并使用Nuclei的模板来扫描漏洞。NucleiFuzzer简化了这个过程,使安全专业人员和Web开发人员更容易有效地检测和解决安全风险。下载NucleiFuzzer以保护您的Web应用程序免受漏洞和攻击。
以上是项目本身的介绍,通过介绍不然发现,这个项目是一个类似于爬虫+漏扫联动类的项目,很像我们在攻防打点过程中经常使用的爬虫+Xray,这里它使用的Nuclei和自写的简单爬虫工具Paramspider,由于本人对于nuclei的使用较少,理解也很一般,我印象的nuclei就是扫poc的,对于常规的漏洞趋近于不支持,但是它竟然能用来fuzzing嘛?于是引起了我的兴趣,开始研究。
在项目readme中,我们可以看到用到的工具和模板,那么有经验的安服仔在这里基本就能看出来了,它这个项目的本质就是一个工具的联动脚本。




工具一个是它自己写的爬虫,一个是Nuclei,暂时不看,先看看用到的模板吧!

fuzzing-templates

跳转来到项目,发现它这是一个fork的项目,原项目是nuclei团队自己的!原来nuclei自己提供了fuzzing的模板,怪我对它的了解太少。

直接进入到原项目,先看readme:

所以我们要使用这个template需要输入的URL是带有参数的,那么是不是可以认为NucleiFuzzer这个项目中,作者自写的爬虫工具就是为了得到带有参数的URL呢?我们暂时先不管,先点击一个具体的模板分析分析,到底是如何做到fuzzing的。
[color=rgba(0, 0, 0, 0.9)]查看xxe/fuzz-xxe.yaml内容如下:


  1. id: fuzz-xxe

  2. info:
  3.   name: XXE Fuzzing
  4.   author: pwnhxl
  5.   severity: medium
  6.   reference:
  7.     - https://github.com/andresriancho/w3af/blob/master/w3af/plugins/audit/xxe.py
  8.   tags: dast,xxe

  9. variables:
  10.   rletter: "{{rand_base(6,'abc')}}"

  11. http:
  12.   - method: GET
  13.     path:
  14.       - "{{BaseURL}}"

  15.     payloads:
  16.       xxe:
  17.         - '<!DOCTYPE {{rletter}} [ <!ENTITY {{rletter}} SYSTEM "file:///c:/windows/win.ini"> ]><x>&{{rletter}};</x>'
  18.         - '<!DOCTYPE {{rletter}} [ <!ENTITY {{rletter}} SYSTEM "file:////etc/passwd"> ]><x>&{{rletter}};</x>'

  19.     fuzzing:
  20.       - part: query
  21.         keys-regex:
  22.           - "(.*?)xml(.*?)"
  23.         fuzz:
  24.           - "{{xxe}}"

  25.       - part: query
  26.         values:
  27.           - "(<!DOCTYPE|<?xml|%3C!DOCTYPE|%3C%3Fxml)(.*?)>"
  28.         fuzz:
  29.           - "{{xxe}}"

  30.     stop-at-first-match: true
  31.     matchers-condition: or
  32.     matchers:
  33.       - type: regex
  34.         name: linux
  35.         part: body
  36.         regex:
  37.           - 'root:.*?:[0-9]*:[0-9]*:'

  38.       - type: word
  39.         name: windows
  40.         part: body
  41.         words:
  42.           - 'for 16-bit app support'
复制代码


只需要看http和matchers部分即可,一个是payload,一个是结果判断。
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]HTTP标签:
[color=rgba(0, 0, 0, 0.9)]HTTP请求部分
·      方法:GET
·      路径:使用变量{{BaseURL}}作为目标URL的基础路径,即传入的带有参数的URL
[color=rgba(0, 0, 0, 0.9)]负载
·      XXE:定义了两个XXE攻击的负载。这些负载使用了DOCTYPE声明来定义一个实体,该实体尝试读取系统文件(如win.ini或/etc/passwd)。
[color=rgba(0, 0, 0, 0.9)]fuzzing
·      part:指定模糊测试应用在HTTP请求的查询部分(query)。
·      keys-regex:定义了用于匹配包含"xml"的查询参数的正则表达式。
·      values:提供了正则表达式,用于匹配可能的XML声明或DOCTYPE声明。
·      fuzz:使用上面定义的xxe负载进行模糊测试。
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]matchers标签:
·      stop-at-first-match:为真,表示一旦匹配成功就停止。
·      matchers-condition:或,意味着满足任一匹配器就视为成功。
·      type:使用正则表达式(regex)和单词(word)两种类型的匹配器。
·      part:指定在响应体(body)中进行匹配。
·      words:提供了用于检测XXE漏洞的正则表达式和单词列表,如检查是否包含系统文件内容(例如/etc/passwd文件的内容)。
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]那么在分析完这个模板内容之后就十分清晰了,与一般的nuclei模板不同的地方就是增加了这个fuzzing标签部分,这部分可以粗略的理解为定义了哪些参数或使用正则匹配哪些内容,使用定义的fuzzing payload进行测试,其他的与一般的模板并无不同,其他的fuzzing-templates也是相同的原理。
[color=rgba(0, 0, 0, 0.9)]简单测试下这个模板:
nuclei.exe -t fuzzing\ -u http://127.0.0.1/test?id=1 -v

可以看到跟我们分析的基本一致,替换URL中的参数,进行发包FUZZING。
[color=rgba(0, 0, 0, 0.9)]那么我们分析完之后,阶段性总结一下,现在发现的问题:
[color=rgba(0, 0, 0, 0.9)]1.要使用fuzzing-templates需要提供的URL必须是带有参数的,我们要想点子获取带参数的URL;
[color=rgba(0, 0, 0, 0.9)]2.模板本身目前全部仅支持GET请求;

[color=rgba(0, 0, 0, 0.9)]问题2目前来说我并没有解决的能力,毕竟这已经是官方自己的模板文件了,看来要实现POST的fuzzing还是要使用ffuf、burp这一类的工具啊。但此时问题1还是有解决办法的,我们是从NucleiFuzzer这个项目调过来的,工具中存在一个ParamSpider的,那么我们就顺势开始这个工具的分析吧。
ParamSpider
[color=rgba(0, 0, 0, 0.9)]先说结论,这是一个比较简陋的爬虫小工具,甚至于它并不能称为爬虫工具。
[color=rgba(0, 0, 0, 0.9)]项目结构如下:

直接丢给GPT完成的分析,那么主要的功能部分就是core部分,我们将core部分除init外的3个主要文件进行分析。
[color=rgba(0, 0, 0, 0.9)]按照一般的处理流程:requester(请求)——>extractor(提取)——>save_it(保存),保存函数不进行分析,尝试对请求和提取进行分析。
·      requester

上面是定义UA头,并随机选择,下面是对URL发起请求,获取response的内容,试用raise_for_status()处理错误的返回状态码,如404或500,最后是一系列的异常处理操作。得出结论这就是个纯粹的request,可以说是没有进行其他任何操作。
·      extractor


传入四个参数:response, level, black_list, placeholder,分别代表要从中提取 URL 的字符串、提取级别,控制提取的详细程度、一个包含不应包含在最终 URL 中的扩展名或词的列表、用于替换 URL 中参数值的占位符,其中response就是requester的结果,其余均是通过主函数的命令行参数进行控制。具体的执行逻辑如下:
[color=rgba(0, 0, 0, 0.9)]1.使用正则表达式提取 URL:
o   set(re.findall(...))用于获取所有匹配项的唯一集合(移除重复项)。
o   函数使用正则表达式 r'.*?:\/\/.*\?.*\=[^$]' 来匹配含有查询参数的 URL。这个表达式匹配形式为 http://someurl.com?param=value 的字符串。
[color=rgba(0, 0, 0, 0.9)]2.处理每个匹配的URL:
o   找到第一个和第二个等号(=)的位置,这些位置标识了参数值的开始。
o   遍历每个提取的URL。
[color=rgba(0, 0, 0, 0.9)]3.应用黑名单过滤:
o   如果black_list 非空,将使用它来构建一个正则表达式,并检查每个 URL 是否包含黑名单中的任何词。如果 URL 包含黑名单中的词,则跳过该 URL。
[color=rgba(0, 0, 0, 0.9)]4.参数替换:
o   如果 level设置为 'high',则对第二个参数值也进行替换。
o   用placeholder 替换 URL 中的参数值。例如,如果placeholder 是 XXX,则 URLhttp://example.com?page=1 会变成http://example.com?page=XXX
[color=rgba(0, 0, 0, 0.9)]5.返回结果:
o   返回处理后的URL 列表,确保唯一性(移除重复项)。
[color=rgba(0, 0, 0, 0.9)]看完这两个函数之后基本就理解了它整个脚本工具的作用了,请求地址,然后通过正则提取出带有参数的URL,主函数中存在黑名单、等级和占位符等功能,除了黑名单外,实际可有可无。
[color=rgba(0, 0, 0, 0.9)]值得一提的是主函数中提供了—subs参数提供了针对于子域名的查询,作者采用的方法是访问https://web.archive.org/,通过互联网档案馆的查询功能,查询*.domain,来获取子域名,属于是一个我没见过的思路了。

那么分析到这里,一个想法就出来了,这不是不如我使用rad或者crawlergo进行爬虫获取URL,然后通过判断URL是否存在=?,提取出带有参数的URL,而后调用Nuclei进行扫描呢?

代码实现
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]目前我个人使用的较多联动脚本是https://github.com/timwhitez/crawlergo_x_XRAY,基本逻辑就是使用crawlergo进行爬虫,工具本身存在--push-to-proxy参数,此时将代理设置为xray被动监听的地址,就实现了爬虫+被动扫描的联动。
[color=rgba(0, 0, 0, 0.9)]Nuclei本身不是一款被动扫描工具,但是这都不是问题,你不能被动扫描,我把爬虫结果保存下来然后主动扫描不就行了。
[color=rgba(0, 0, 0, 0.9)]而后我又想到了另一个问题,把带有参数的URL使用fuzzing-templates进行fuzzing了,那不带有参数的呢?是不是我直接用常用的poc进行一个基础扫描呢?
总体设计
[color=rgba(0, 0, 0, 0.9)]
1.   设置三个参数-u,-f,-m,代表单个url扫描,读取文件获取url进行扫描,m用来控制模式
2.  功能函数包括爬虫,添加http头和三个模式的扫描函数,分为fuzz、common和all
3.  创建parma目录存爬取到的参数URL,创建common目录存取不带有参数的URL,创建result存取扫描结果
[color=rgba(0, 0, 0, 0.9)]流程图如下:


简单、明了、没有难度。但是实际上我在写的过程中碰到了各种各样的问题,也做了各种各样的完善和考虑,我在这里分享出来,希望各位大佬指点。
爬虫函数
[color=rgba(0, 0, 0, 0.9)]
1.   构造crawlergo命令并执行
2.  获取爬虫结果
3.  对结果进行进一步过滤并保存到文件中
[color=rgba(0, 0, 0, 0.9)]针对爬虫的处理参考crawlergo自身项目的readme:


可以看到项目本身提供了一个示例的python调用demo,我们按照这样直接操作即可。
[color=rgba(0, 0, 0, 0.9)]即执行完命令后,获取req_list的结果,而后获取req_list[’url’]的结果,针对url进行判断,是否存在?=,而后按照结果进行保存,保存的文件的格式采用url_parma.txt和url_common.txt用来区分目标和爬取到的url是否带有参数,由于文件名中不能存在://等字符,均直接替换为下划线:

  1. def crawl_url(url):
  2.   """爬虫函数"""
  3.   print(f"{bcolors.OK}----开始爬虫----{bcolors.ENDC}")
  4.   
  5.   # 构造文件名
  6.   param_filename = './parma/' + url.replace('http://', '').replace('https://', '').replace('/', '_').replace(':', '_') + "_parma.txt"
  7.   common_filename = './common/' + url.replace('http://', '').replace('https://', '').replace('/', '_').replace(':', '_') + "_common.txt"
  8.   
  9.   # 构造crawlergo命令并执行
  10.   cmd = [crawlergo_path, "-c", chrome_path,"-t", "5","-f","smart","--fuzz-path","--custom-headers",json.dumps(get_random_headers()), "--output-mode", "json" , url]
  11.   rsp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  12.   output, error = rsp.communicate()
  13.   try:
  14.     # 处理爬虫结果
  15.     result = simplejson.loads(output.split("--[Mission Complete]--")[1])
  16.   except:
  17.     print(f"{bcolors.FAIL}爬虫失败{bcolors.ENDC}")
  18.     return None, None
  19.   req_list = result["req_list"]
  20.   print(f"{bcolors.OK}----爬虫结束,开始处理爬取结果----{bcolors.ENDC}")
  21.   if req_list:
  22.     for req in req_list:
  23.       url = req['url']
  24.       if '=' in url and '?' in url:
  25.         with open(param_filename, 'a') as f:
  26.           f.write(url + '\n')
  27.       else:
  28.         with open(common_filename, 'a') as f:
  29.           f.write(url + '\n')
  30.   return param_filename, common_filename
复制代码

针对输出方面,由于爬虫的输出实在是太多了,输出到控制台我觉得很影响观感,所以直接没有输出,当然大家可以自己进行调整。

工具函数
[color=rgba(0, 0, 0, 0.9)]在写的过程中发现了几点需求问题:
1.  我的脚本中主要依靠命令行调用外部工具,同时还要指定nuclei的模板路径;
2.  针对网站的爬虫,并不一定存在带有参数的URL;
3.  从文件读取的URL可能存在纯domain或者IPORT类型,需要手动添加http://或者https://头
4.  作为一名安服选手,所有的http发包请求都应该添加随机生成UA头。
5.  针对爬取到的url和结果,需要先判断文件夹是否存在,不存在就创建
[color=rgba(0, 0, 0, 0.9)]我将这些的解决统一都写在这一章节中。
路径问题
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]路径采用读取配置文件的方式解决,定义配置文件config.json,内容如下:

  1. {
  2.     "httpx_path": "",
  3.     "crawlergo_path": "",
  4.     "nuclei_path": "",
  5.     "fuzzing_template_path": "",
  6.     "common_template_path": "",
  7.     "chrome_path": ""
  8. }
复制代码

按照自己的工具路径位置进行填写,同时需要避免中文目录,配置文件中的httpx并没有使用,至于为什么我会在后面说明。
[color=rgba(0, 0, 0, 0.9)]脚本中使用json.load的方式获取相关路径内容:

  1. def get_config():
  2.   with open('config.json', 'r') as f:
  3.     config = json.load(f)
  4.   return config

  5. config = get_config()
  6. httpx_path = config['httpx_path']
  7. crawlergo_path = config['crawlergo_path']
  8. nuclei_path = config['nuclei_path']
  9. fuzzing_template_path = config['fuzzing_template_path']
  10. common_template_path = config['common_template_path']
  11. chrome_path = config['chrome_path']
复制代码

爬虫结果问题
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]爬虫扫描不一定有结果,在运行函数中添加判断,如果存在再去调用相应的扫描函数,不存在就输出提示

  1. if os.path.exists(param_filename) and process_func in [process_url_fuzz, process_url_all]:
  2.     process_func(url, *needed_params)
  3. elif os.path.exists(common_filename) and process_func in [process_url_common, process_url_all]:
  4.     process_func(url, *needed_params)
  5. else:
  6.     print(f"{bcolors.FAIL}无法以{args.mode}处理{url},因为没有足够的数据{bcolors.ENDHTTC}")
复制代码

HTTP协议头问题
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]读取传入的内容添加判断即可
  1. def getAllrequest(target_url):
  2.   """添加http头"""
  3.   # url检测,传入的url是否为完整的域名,若仅为IP+port需要添加协议头
  4.   isHTTPS = True  # 将是否为https首先标志为True
  5.   if ("https" not in target_url) & ("http" not in target_url):
  6.     try:
  7.       url = "https://" + target_url
  8.       requests.packages.urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)  # 忽略ssl验证警告
  9.       requests.get(url=url, verify=False, timeout=3)  # 设置忽略ssl证书安全性警告,设置3s的延迟保证程序健壮性
  10.     except Exception as e:
  11.       isHTTPS = False
  12.     finally:
  13.       if isHTTPS:
  14.         target_url = "https://" + target_url
  15.       else:
  16.         target_url = "http://" + target_url
  17.   return target_url
复制代码
[color=rgba(0, 0, 0, 0.9)]
随机UA头
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]采用fake_useragent获取相应的UA头
[color=rgba(0, 0, 0, 0.9)]
  1. from fake_useragent import UserAgent

  2. ua = UserAgent()


  3. def get_random_headers():
  4.   headers = {'User-Agent': ua.random}

  5.   return headers
复制代码

保存路径
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]创建判断函数,而后传入进行判断即可


  1. def ensure_directories_exist(directories):
  2.   """Ensure that the given directories exist, create them if not."""
  3.   for directory in directories:
  4.     if not os.path.exists(directory):
  5.       os.makedirs(directory)

  6. # 主函数中
  7. # Check if directories exist, create if not
  8.   ensure_directories_exist(['./parma/', './common/', './result/'])
复制代码


扫描函数
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]针对不同的mode模式调用不同的扫描函数,具体内容应该基本一致,唯一需要注意的就是保存nuclei扫描结果的报告命名方式,这里我同样采用url_common.json或者url_fuzz.json的方式进行保存
  1. def process_url_common(url, common_filename):
  2.   """common模式扫描"""
  3.   # 定义输出文件名
  4.   output_common_filename = url.replace('http://', '').replace('https://', '').replace('/', '_').replace(':', '_') + "_common.json"

  5.   print(f'{bcolors.OK}----开始扫描{common_filename}----{bcolors.ENDC}')
  6.   cmd_httpx_nuclei = f"{nuclei_path} -t {common_template_path} -l {common_filename} -o ./result/{output_common_filename}"
  7.   print(f"{bcolors.OKBLUE}{cmd_httpx_nuclei}{bcolors.ENDC}")
  8.   try:
  9.     rsp = subprocess.Popen(cmd_httpx_nuclei, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  10.     stdout, stderr = rsp.communicate()
  11.     print(stdout)
  12.     print(stderr)
  13.     print(f"{bcolors.OK}----{url},common扫描结束----{bcolors.ENDC}")
  14.   except FileNotFoundError as e:
  15.     print(f"{bcolors.FAIL}Error: {e}{bcolors.ENDC}")
复制代码

nuclei的扫描结果我选择了输出,不然这个不输出内容,感觉脚本一直就是处于卡死的状态。
[color=rgba(0, 0, 0, 0.9)]同时在这里我也回答一下为什么没有httpx的问题,按照我最开始的设想,爬取到的URL,我先使用httpx进行验活处理,提取出200 301 403 401这些返回码,但是很不幸的是subprocess.Popen,在windows环境下,对于管道符的处理存在问题,下面是我本来想执行的命令:

type url_parma.txt | httpx -silent -mc 200,301,302,403|nuclei ...
[color=rgba(0, 0, 0, 0.9)]此时发现第一个问题,我的url_parma.txt的格式是./parma/url_parma.txt,提示语法命令不正确:
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]
这都是小事,我把/换成\呗

但是在后续调试过程中,我发现了,管道符没有起作用,它不给后面的命令了,我如果直接使用windows的cmd直接运行这个命令是没问题的,但是使用subprocess.Popen,进行一次输出调试,直接输出内容了,并没有往后面执行,因此我选择直接放弃,httpx的任务就交给域名收集之后大家手动完成吧。
[color=rgba(0, 0, 0, 0.9)]我的all模式扫描函数,选择的方式是直接调用fuzz和common两个扫描函数,因此在判断爬虫结果的处理上还是存在问题,我索性在这个函数中重新进行了一次判断:

  1. def process_url_all(url, param_filename, common_filename):
  2.   """all模式扫描"""
  3.   if os.path.exists(param_filename):
  4.     process_url_fuzz(url, param_filename)
  5.   else:
  6.     print(f"{bcolors.FAIL}{param_filename} does not exist{bcolors.ENDC}")

  7.   if os.path.exists(common_filename):
  8.     process_url_common(url, common_filename)
  9.   else:
  10.     print(f"{bcolors.FAIL}{common_filename} does not exist{bcolors.ENDC}")
复制代码

主函数
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]主函数主要存在一个问题——不优雅,为什么不优雅?因为我的判断实在太多了,每个扫描函数需要的参数不一样,首先判断需要哪些参数从爬虫的返回里面去取,还要判断文件内容是否存在,如果爬虫没有结果就会导致后面的内容报错。
[color=rgba(0, 0, 0, 0.9)]那么这一部分主要依靠github copilot的建议完成,定义一个字典:


  1. process_funcs = {
  2.     'fuzz': (process_url_fuzz, [0]),
  3.     'common': (process_url_common, [1]),
  4.     'all': (process_url_all, [0, 1])
  5.   }
  6. process_func, param_indices = process_funcs[args.mode]
  7. needed_params = [all_params[i] for i in param_indices]
  8.     if os.path.exists(param_filename) and process_func in [process_url_fuzz, process_url_all]:
  9.       process_func(url, *needed_params)
  10.     elif os.path.exists(common_filename) and process_func in [process_url_common, process_url_all]:
  11.       process_func(url, *needed_params)
  12.     else:
  13.       print(f"{bcolors.FAIL}无法以{args.mode}处理{url},因为没有足够的数据{bcolors.ENDC}")
复制代码

其余部分就不进行展示,其实基本也已经全部展示完了,大家还可以自己添加多任务功能什么的。

小彩蛋
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]想着写都写了,直接给输出提示字符写个颜色,一开始是这么写的:

  1. class bcolors:
  2.   FAIL = '\033[91m'
  3.   OK = '\033[92m'
  4.   INFO = '\033[94m'
  5.   ENDC = '\033[0m'

  6. print(f"{bcolors.OK}----开始爬虫----{bcolors.ENDC}")
复制代码

发现用这样的方式windows的cmd显示不出来


那么我就继续问AI吧(没有AI我什么都不是啊),解决方案是使用python的colorama库
  1. from colorama import init, Fore, Back, Style

  2. class bcolors:
  3.     HEADER = Fore.CYAN
  4.     OKBLUE = Fore.BLUE
  5.     OK = Fore.GREEN
  6.     WARNING = Fore.YELLOW
  7.     FAIL = Fore.RED
  8.     ENDC = Fore.RESET
  9.     BOLD = Style.BRIGHT
  10.     UNDERLINE = Style.DIM

  11. print(f"{bcolors.OK}----开始爬虫----{bcolors.ENDC}")
复制代码



总结
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]在这篇文章中,我从一个项目的分析作为开始,尝试进行自己的实现与改造。项目是非常简单的项目,思路也是经常碰到的思路,分享自己的问题,也是为了让大家少踩坑,同时存在不足的地方也欢迎大家补充。

[color=rgba(0, 0, 0, 0.9)]

继续一个彩蛋
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]当我想着整理自己的common模板时,我突然想到了攻防中最重要的fastjson和shiro反序列化,当然我也在官方的模板中发现了有大佬提交了。
[color=rgba(0, 0, 0, 0.9)]但是我在调试shiro反序列化的模板时发现了问题,就是扫不出来啊,我首先用fofa提取了1W个ruoyi,它一个没扫出来,然后我受不了了,开了一个vulhub,它竟然也没扫出来,那这样我就觉得不对了。


模板的内容如上,采用的payload就是shiro_encrypted_keys.txt这个文档中的内容,那么我们看看文档中的内容:


payload字典每一行实际上是key:payload的内容,我们知道shiro的加密方式是aes然后base64,我们随便取一行用相同的方式解密就能得到加密的原材料,就是原始的payload,然后用你自己的key加密,这样就能扩展这个字典了(都不能用,先想着怎么加字典了)。然后我们发现,模板中是直接读取这里面的内容,也就是说我们发送的payload是rememberMe=key:payload,这明显不对吧,直接删除key:,重新扫一下:


好了,扫出来了,但是实际上如果真正要用的话,是不是只用shiro-detect.yaml,判断是否存在shiro框架,然后用其他自动化工具再扫描会不会更好呢?



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-27 22:39 , Processed in 0.014757 second(s), 19 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表