前情提要:小改ARL - zgbsm’s note
大伙可能还记得上次我最后画了一个并发限制模型
其实如果要限制httpx和nuclei,使他们俩运行的时候不存在并发的情况,只需给它俩分别上个互斥锁就行了,不用并发限制器这么麻烦,但是也不是说并发限制器一无是处,我们可以把并发限制器包装一下,实现那种并发数的弹性伸缩;不过弹性伸缩对于我“小改ARL”这样的单兵工具来说还是太抽象了,而且我发现其实爆内存的问题不是nuclei并发导致的,而是反复初始化nuclei导致的
哦对,中间插一句,我给我的“小改ARL”起了个名字,叫ARLite(Assault Rifle Lite),叫它“小改ARL”也行,毕竟也属于AR系的
继续讨论nuclei的问题,我不清楚nuclei是不是设计上不推荐反复初始化,我也懒得去研究了,干脆把nuclei模块独立成一个可执行文件,每次运行完一个任务之后把进程杀掉,这样就可以回收内存;根据之前稳定性测试的经验,nuclei刚起来的时候基本上只需要300M-400M内存空间,如果让它只跑一个任务的话,一直到进程退出,这个内存占用是基本不会变的
于是最终版本的ARLite结构设计图变成了这个:
因为nuclei是最后运行的,所以社畜的工作就是把端口扫描、站点识别、服务识别、npoc跑完,然后把nuclei的目标列表存到mongo里面,然后nuclei调度器会每隔5秒取一个任务出来,把任务ID作为参数运行nuclei模块,nuclei模块再连接mongo数据库取任务详情,跑完之后再把任务结果存回mongo里,最后由nuclei模块更新任务状态(其实更新状态的事情最好由调度器去做)
目前ARLite已经稳定运行3天,其中有一次被chrome爆了内存(物理内存跑满,使用了swap分区),但是chrome爆内存比较温柔,整个系统不会卡死,卡个2分钟chrome运行超时就自己恢复了;平时基本上整个系统(Debian 11)加上mongo和ARLite的内存占用大约是1.1G-1.4G左右,2核2G的服务器可以说是游刃有余
其实目前的设计有一个很重要的好处,就是站点识别不会被nuclei漏扫阻塞,指纹识别、npoc等工作可以很快完成,然后大伙就可以先看站,等看完站nuclei也差不多该跑完了
大伙可能还记得原版ARL有一个自动打标签的功能,大概在这个位置会显示网站标签
就是那个“添加标签”的位置,原版ARL会显示“无效”、“入口”之类的,其实我觉得这里也可以放点指纹信息,大伙可能会说,ARL不是右边有一个显示指纹的地方吗!我觉得那个地方主要还是放自动探测的指纹吧,因为那个感觉都不怎么准,标签这个位置可以放点致命的,比如若依、ZLMediaKit、XXL-JOB或者其他一些喜欢爆弱口令、RCE、目录穿越的系统
这个地方的自动打标签功能可以做到站点识别功能里面,因为httpx的识别结果还是很详细的,我们可以学nuclei,用yaml模板进行识别,这里是识别华为云waf的一个yaml文件例子
name: '华为waf'
action: 'drop'
tag: ''
finger:
title: '未找到'
status: 404
header:
- 'Server: CloudWAF'配置文件的action设置为“drop”,则在站点识别的时候会直接将匹配的识别结果丢弃,这样就不会存储无效的页面了
目前主要的功能、稳定性相关开发改进工作基本就告一段落了,接下来就是Web功能接口的完善和性能优化之类的,再实战个几轮应该还会有新的需求提出来,到时候再说
大伙如果还想看,我可以简单介绍一下我发病的时候画的弹性伸缩功能设计;因为是发病的时候画的,所以没有实际写代码测试过,有可能出现“根本飞不起来”的情况,大伙看个热闹就好
图中的“并发小子”就是我们之前ARLite设计图里面的许多个社畜线程,每次社畜要接任务干活的时候需要调用“起飞申请”函数,“起飞申请”函数会先上一个读锁,然后检查溢出缓存里面有没有数据,如果有数据,就先把溢出缓存的数据取出来写进并发限制器里,然后再写自己的,写成功之前,“起飞申请”函数将会一直阻塞,写成功之后释放读锁
“并发小子”在执行完当前任务之后,调用“降落”函数,“降落”函数会先上一个读锁,然后从chan interface{}里面取东西,取完之后释放读锁并返回
“性能监视器”会一直监控系统内存资源,当资源充裕的时候,性能监视器会扩大chan interface{}的容量,但是因为存在并发的情况,在扩大容量时如果有线程在读写,可能会出现不可预料的后果,因此“性能监视器”会先上一个写锁,然后再对chan interface{}进行容量修改操作。容量修改操作有“变胖”和“减肥”两种情况
“变胖”是最容易的,直接上写锁、重新make一个更大容量的chan,然后把原先的chan数据导过来,释放写锁即可
“减肥”可能就比较痛苦,因为chan interface{}里面的数据量可能比减肥目标要大,比如说我现在内存少了,我希望减少一点并发数,那么性能监视器会先上写锁,再将chan interface{}的容量调整到1,但是目前chan里面有2个数据,怎么办呢?并发限制器可以将溢出的那个数据取出来,放到溢出缓存里面,然后释放写锁;每隔一段时间性能监视器就上个写锁,然后看看并发限制器有没有空间,有空间就把溢出缓存里面的东西取出来写进并发限制器里,然后释放写锁
目前用我眼睛看这套设计应该是没问题的,但是“并发人的事情”很难说,要测试之后才知道可不可行