XSS 扫描器成长记

时间:2020年2月12日

为了实现自动刷SRC的目标,过年前就开始对w13scan的xss扫描功能进行优化,灵感来源于xray所宣称的基于语义的扫描技术。

之前xss扫描是参考w3af中的源码,原理也很简单就是暴力的使用xss的payload进行请求,最后在返回文本中查找关键字xss payload一般有以下几个部分。

后面我认真的学习了一下Xsstrike、Xray、Awvs中的检测技巧以及检测参数,想将它们的优点和为一体。

01 XSStrike

先说说Xsstrike,里面带有xss扫描和fuzz,但感觉xss扫描的效果也不是太理想。不过它的一些特性还是可以学习的。

DOM XSS

Xsstrike的dom扫描,是通过正则来分析敏感函数

sources = r'''document\.(URL|documentURI|URLUnencoded|baseURI|cookie|referrer)|location\.(href|search|hash|pathname)|window\.name|history\.(pushState|replaceState)(local|session)Storage'''    sinks = r'''eval|evaluate|execCommand|assign|navigate|getResponseHeaderopen|showModalDialog|Function|set(Timeout|Interval|Immediate)|execScript|crypto.generateCRMFRequest|ScriptElement\.(src|text|textContent|innerText)|.*?\.onEventName|document\.(write|writeln)|.*?\.innerHTML|Range\.createContextualFragment|(document|window)\.location'''    scripts = re.findall(r'(?i)(?s)]*>(.*?)', response)

通过将script脚本内的内容提取出来,通过一些正则来获取,最后输出。但这种方式准确度很低,只能用于辅助,不太适合自动化扫描。

内置参数

它里面有内置一些参数,在检测时会将这些参数也一起发送

blindParams = [  # common paramtere names to be bruteforced for parameter discovery    'redirect', 'redir', 'url', 'link', 'goto', 'debug', '_debug', 'test', 'get', 'index', 'src', 'source', 'file',    'frame', 'config', 'new', 'old', 'var', 'rurl', 'return_to', '_return', 'returl', 'last', 'text', 'load', 'email',    'mail', 'user', 'username', 'password', 'pass', 'passwd', 'first_name', 'last_name', 'back', 'href', 'ref', 'data', 'input',    'out', 'net', 'host', 'address', 'code', 'auth', 'userid', 'auth_token', 'token', 'error', 'keyword', 'key', 'q', 'query', 'aid',    'bid', 'cid', 'did', 'eid', 'fid', 'gid', 'hid', 'iid', 'jid', 'kid', 'lid', 'mid', 'nid', 'oid', 'pid', 'qid', 'rid', 'sid',    'tid', 'uid', 'vid', 'wid', 'xid', 'yid', 'zid', 'cal', 'country', 'x', 'y', 'topic', 'title', 'head', 'higher', 'lower', 'width',    'height', 'add', 'result', 'log', 'demo', 'example', 'message']

很好的思路,后面我的扫描器中也使用了这一点,从乌云镜像XSS分类中提取出了top10参数,在扫描时也会将这些参数加上。

HTML解析&分析反射

如果参数可以回显,那么通过html解析就可以获得参数位置,分析回显的环境(比如是否在html标签内,是否在html属性内,是否在注释中,是否在js中)等等,以此来确定检测的payload。

后面我的扫描器的检测流程也是这样,非常准确和效率,不过Xsstrike分析html是自己写的分析函数,刚开始我也想直接用它的来着,但是这个函数内容过多,调试困难,代码也很难理解。

其实如果把html解析理解为html的语义分析,用python3自带的html提取函数很容易就能完成这一点。

02 Xray

XSStrike让我学习到了新一代xss扫描器应该如何编写,但新一代xss扫描器的payload是在Xray上学到的。

由于Xray没有开源,所以就通过分析日志的方式来看它的工作原理。

准备工作

简单写了一个脚本,用来分别测试xss在script,style内,html标签内,注释这几种情况下xray的发包过程。

发包探索

1.对于在script的脚本内的回显内容,对于以下case

   

xray顺序发送了以下payload:pdrjzsqc,"-pdrjzsqc-",

最后会给出payload,但这个包并没有发送。后面把prompt作为关键词屏蔽,发现最后还是给出这个payload。

还有一种情况,在script中的注释中输出

                      

xray会发送`\n;chxdsdkm;//`来判定,最后给出payload `\n;prompt(1);//`

2.对于在标签内的内容,对于以下case

   

xray顺序发送了以下payload:spzzmsntfzikatuchsvu,,当确定尖括号没有被过滤时,会继续发送以下payload:sCrIpTjhymehqbkrScRiPt,iMgSrCoNeRrOrjhymehqbkr>,SvGoNlOaDjhymehqbkr>,IfRaMeSrCjAvAsCrIpTjhymehqbkr>,aHrEfJaVaScRiPtjhymehqbkrClIcKa,iNpUtAuToFoCuSoNfOcUsjhymehqbkr>,进行关键词的试探,最后给出payload为

3.对于在style里内容,以下case

   " />

xray顺序发送了以下payload:kmbrocvz,expression(a(kmbrocvz))

4.对于在html标签内的内容,以下case

   "/>

xray顺序发送了以下payload:spzzmsntfzikatuchsvu,"ljxxrwom=",'ljxxrwom=',ljxxrwom=,当确认引号没有被过滤时,会继续发送以下payload:">,">ScRiPtvkvjfzrtgiScRiPt,">ImGsRcOnErRoRvkvjfzrtgi>,">SvGoNlOaDvkvjfzrtgi>,">iFrAmEsRcJaVaScRiPtvkvjfzrtgi>,">aHrEfJaVaScRiPtvkvjfzrtgicLiCkA,">InPuTaUtOfOcUsOnFoCuSvkvjfzrtgi>," OnMoUsEoVeR=xviinqws,最后可以确定payload为">

,"OnMoUsEoVeR=prompt(1)//

如果针对此类case:

   " />

xray返回payload为prompt(1),说明xray会把onerror后面的内容当作JavaScript脚本来执行,如果把onerror改为onerror1,同样会返回prompt。在awvs规则中也看到过类似的规则

   parName == "ONAFTERPRINT" ||                                parName == "ONBEFOREPRINT" ||                                parName == "ONBEFOREONLOAD" ||                                parName == "ONBLUR" ||                                parName == "ONERROR" ||                                parName == "ONFOCUS" ||                                parName == "ONHASCHANGE" ||                                parName == "ONLOAD" ||                                parName == "ONMESSAGE" ||                                parName == "ONOFFLINE" ||                                parName == "ONONLINE" ||                                parName == "ONPAGEHIDE" ||                                parName == "ONPAGESHOW" ||                                parName == "ONPOPSTATE" ||                                parName == "ONREDO" ||                                parName == "ONRESIZE" ||                                parName == "ONSTORAGE" ||                                parName == "ONUNDO" ||                                parName == "ONUNLOAD" ||                                parName == "ONBLUR" ||                                parName == "ONCHANGE" ||                                parName == "ONCONTEXTMENU" ||                                parName == "ONFOCUS" ||                                parName == "ONFORMCHANGE" ||                                parName == "ONFORMINPUT" ||                                parName == "ONINPUT" ||                                parName == "ONINVALID" ||                                parName == "ONRESET" ||                                parName == "ONSELECT" ||                                parName == "ONSUBMIT" ||                                parName == "ONKEYDOWN" ||                                parName == "ONKEYPRESS" ||                                parName == "ONKEYUP" ||                                parName == "ONCLICK" ||                                parName == "ONDBLCLICK" ||                                parName == "ONDRAG" ||                                parName == "ONDRAGEND" ||                                parName == "ONDRAGENTER" ||                                parName == "ONDRAGLEAVE" ||                                parName == "ONDRAGOVER" ||                                parName == "ONDRAGSTART" ||                                parName == "ONDROP" ||                                parName == "ONMOUSEDOWN" ||                                parName == "ONMOUSEMOVE" ||                                parName == "ONMOUSEOUT" ||                                parName == "ONMOUSEOVER" ||                                parName == "ONMOUSEUP" ||                                parName == "ONMOUSEWHEEL" ||                                parName == "ONSCROLL" ||                                parName == "ONABORT" ||                                parName == "ONCANPLAY" ||                                parName == "ONCANPLAYTHROUGH" ||                                parName == "ONDURATIONCHANGE" ||                                parName == "ONEMPTIED" ||                                parName == "ONENDED" ||                                parName == "ONERROR" ||                                parName == "ONLOADEDDATA" ||                                parName == "ONLOADEDMETADATA" ||                                parName == "ONLOADSTART" ||                                parName == "ONPAUSE" ||                                parName == "ONPLAY" ||                                parName == "ONPLAYING" ||                                parName == "ONPROGRESS" ||                                parName == "ONRATECHANGE" ||                                parName == "ONREADYSTATECHANGE" ||                                parName == "ONSEEKED" ||                                parName == "ONSEEKING" ||                                parName == "ONSTALLED" ||                                parName == "ONSUSPEND" ||                                parName == "ONTIMEUPDATE" ||                                parName == "ONVOLUMECHANGE" ||                                parName == "ONWAITING" ||                                parName == "ONTOUCHSTART" ||                                parName == "ONTOUCHMOVE" ||                                parName == "ONTOUCHEND" ||                                parName == "ONTOUCHENTER" ||                                parName == "ONTOUCHLEAVE" ||                                parName == "ONTOUCHCANCEL" ||                                          parName == "ONGESTURESTART" ||                                parName == "ONGESTURECHANGE" ||                                parName == "ONGESTUREEND" ||                                parName == "ONPOINTERDOWN" ||                                parName == "ONPOINTERUP" ||                                parName == "ONPOINTERCANCEL" ||                                parName == "ONPOINTERMOVE" ||                                parName == "ONPOINTEROVER" ||                                parName == "ONPOINTEROUT" ||                                parName == "ONPOINTERENTER" ||                                parName == "ONPOINTERLEAVE" ||                                parName == "ONGOTPOINTERCAPTURE" ||                                parName == "ONLOSTPOINTERCAPTURE" ||                                parName == "ONCUT" ||                                parName == "ONCOPY" ||                                parName == "ONPASTE" ||                                parName == "ONBEFORECUT" ||                                parName == "ONBEFORECOPY" ||                                parName == "ONBEFOREPASTE" ||                                parName == "ONAFTERUPDATE" ||                                parName == "ONBEFOREUPDATE" ||                                parName == "ONCELLCHANGE" ||                                parName == "ONDATAAVAILABLE" ||                                parName == "ONDATASETCHANGED" ||                                parName == "ONDATASETCOMPLETE" ||                                parName == "ONERRORUPDATE" ||                                parName == "ONROWENTER" ||                                parName == "ONROWEXIT" ||                                parName == "ONROWSDELETE" ||                                parName == "ONROWINSERTED" ||                                parName == "ONCONTEXTMENU" ||                                parName == "ONDRAG" ||                                parName == "ONDRAGSTART" ||                                parName == "ONDRAGENTER" ||                                parName == "ONDRAGOVER" ||                                parName == "ONDRAGLEAVE" ||                                parName == "ONDRAGEND" ||                                parName == "ONDROP" ||                                parName == "ONSELECTSTART" ||                                parName == "ONHELP" ||                                parName == "ONBEFOREUNLOAD" ||                                parName == "ONSTOP" ||                                parName == "ONBEFOREEDITFOCUS" ||                                parName == "ONSTART" ||                                parName == "ONFINISH" ||                                parName == "ONBOUNCE" ||                                parName == "ONBEFOREPRINT" ||                                parName == "ONAFTERPRINT" ||                                parName == "ONPROPERTYCHANGE" ||                                parName == "ONFILTERCHANGE" ||                                parName == "ONREADYSTATECHANGE" ||                                parName == "ONLOSECAPTURE" ||                                parName == "ONDRAGDROP" ||                                parName == "ONDRAGENTER" ||                                parName == "ONDRAGEXIT" ||                                parName == "ONDRAGGESTURE" ||                                parName == "ONDRAGOVER" ||                                parName == "ONCLOSE" ||                                parName == "ONCOMMAND" ||                                parName == "ONINPUT" ||                                parName == "ONCONTEXTMENU" ||                                parName == "ONOVERFLOW" ||                                parName == "ONOVERFLOWCHANGED" ||                                parName == "ONUNDERFLOW" ||                                parName == "ONPOPUPHIDDEN" ||                                parName == "ONPOPUPHIDING" ||                                parName == "ONPOPUPSHOWING" ||                                parName == "ONPOPUPSHOWN" ||                                parName == "ONBROADCAST" ||                                parName == "ONCOMMANDUPDATE" ||                                parName == "STYLE"

awvs会比较参数名称来确定。在后面的自动化扫描中,发现这种方式的误报还是很高,最后我将这种情况调整到了awvs的方式,只检测指定的属性key。

从这两处细微的差别可以看到,awvs宁愿漏报也不误报,结果会很准确,xray更多针对白帽子,结果会宽泛一些。

5.对于在html注释内的内容,以下case

   

xray顺序发送了以下payload:spzzmsntfzikatuchsvu,-->,--!>,和上面类似,当确定-->或--!>没有过滤时,会发送

   以 --> 或 --!> 开头,添加如下内容      sCrIpTbvwpmjtngzsCrIpT   ImGsRcOnErRoRbvwpmjtngz>   sVgOnLoAdbvwpmjtngz>   iFrAmEsRcJaVaScRiPtbvwpmjtngz>   aHrEfJaVaScRiPtbvwpmjtngzcLiCkA   InPuTaUtOfOcUsOnFoCuSbvwpmjtngz>

04Awvs

Awvs的扫描规则很多,针对的情况也很多,没有仔细看它的工作方式是怎样的,主要是看它的payload以及检测的情况,和上面两种查漏补缺,最终合成了我的xss扫描器~比如它会对meta标签的content内容进行处理,会对你srcipt,src等tag的属性处理,也有一些对AngularJs等一些流行的框架的XSS探测payload。

05 我的扫描器

我的XSS扫描器就是综合上面三种扫描器而来,如果仔细观察,还会发现上面扫描器的一些不同寻常的细节。

比如xray不会发送xss的payload,都是用一些随机字符来代替,同时也会随机大小写对一些标签名称,属性名称等等。

这些精致的技巧我的扫描器也都一一吸取了,嘿嘿!

扫描流程

我的扫描器扫描流程是这样的

发送随机flag -> 确定参数回显 -> 确定回显位置以及情况(html,js语法解析) -> 根据情况根据不同payload探测 -> 使用html,js语法解析确定是否多出来了标签,属性,js语句等等

使用html语法树检测有很多优势,可以准确判定回显所处的位置,然后通过发送一个随机payload,例如,再使用语法检测是否有Asfaa这个标签,就能确定payload是否执行成功了。

html语法树用python自带的库

from html.parser import HTMLParser

js检测也是如此,如果回显内容在JavaScript脚本中,发送随机flag后,通过js语法解析只需要确定Identifier和Literal这两个类型中是否包含,如果flag是Identifier类型,就能直接判断存在xss,payload是alert(1)//,如果flag是Literal类型,再通过单双引号来闭合进行检测。

Debug之旅

整个xss扫描代码不过1000行,但debug的过程是道阻且长。

本地靶机测试后就对在线的靶机进行了测试

查漏补缺后就开始了自动化扫描。

整个自动化架构如下

1. 提供url -> 爬虫爬取 -> 参数入库 -> 消息队列 -> xss扫描器                                -> 子域名入库                                -> url入库

1.爬虫使用的crawlergo,效果挺不错的,但还是不太满足我的需求(造轮子的心态又膨胀了)2.数据库使用的mongodb3.用celery分布式调用,由于用到了celery,又用到了rabbitmq消息队列,flower监控4.用了server酱进行微信推送(得到一个漏洞微信就会响一次 )

刚开始打把游戏微信就会不停的响,然后就查找误报,优化逻辑 以此往复

经过了不懈的改造,优化了检测逻辑,加入了去重处理后,现在不仅扫描的慢而且推送的消息也变少了 。

06 一些成果

经过一段时间对src的扫描后,成功还是挺多的(很多都归功于爬虫)

甚至发现了微软分站某处xss

未完,待续。。。

往 期 热 门

(点击图片跳转)

觉得不错点个“在看”哦

用python搭建一个校园维基网站(二)—— 可编辑内容的首页的创建

專 欄

treelake ,Python中文社区专栏作者

项目Github地址:

项目总体简介请看

本文可独立使用,创建了一个可编辑内容的首页,展示了wagtail的一些基础用法。文末为本文所创项目文件github地址。 比较详细,新手可尝试,不过最好有一定Django基础。

项目结构概观

1、manage.py是Django项目通用的管理脚本(通过python manage.py 某命令参数使用)。

2、requirements.txt用于存储当前项目的依赖列表(自动生成的为Django和wagtail,虚拟环境(virtualenv)下可用pip freeze >> requirements.txt追加)。

3、genius包含项目主要信息,有主路由(urls.py)、wsgi接口(wsgi.py)、配置文件夹(分基础配置base.py、开发环境配置dev.py与生产环境配置production.py,后二者依赖基础配置)、全局静态资源文件夹(static)与模板资源文件夹(templates)。

4、home是自动生成的app文件夹,包含了models.py页面数据模型和templates模板文件夹。默认生成的models.py中定义了一个简单的HomePage类(继承自wagtail的Page类)来代表一个页面(即默认的欢迎页)的模型(该简单模型的可编辑内容部分只有title字段)。在wagtail的概念中,页面模型和模板文件是默认关联的,如HomePage默认对应的模板为templates/home/home_page.html(注意命名的转换关系),而欢迎页:8000中的大部分内容就在该模板中(该模板使用extends语句继承genius\templates\base.html,并使用block语句填充相应内容)。如下:

5、search则是自动生成的提供搜索功能的app文件夹,由于基于wagtail.wagtailsearch所以只包含了views.py视图文件和templates模板文件夹。暂时不管。

创建wiki主页

我们的WikiHome页面模型中需要图中红色高亮的一系列字段,其中title字段继承自Page类,不用额外添加,image字段为连接到wagtailimages.Image模型的外键。content_panels列表提供了该页面模型在后台管理编辑页面的呈现内容。

此外,对于TopLink和LittleIntros我们需要另外新建两个继承wagtail提供的Orderable(使有序)的非页面模型。

WikiHomeLittleIntros的字段有fontawesome图标类名,小标题和简述,如下图。还包含了一个wagtail提供的对ForeignKey进行了一层封装的ParentalKey外键连接到它所属的WikiHome页面。类似的,panels表明出现在可编辑区。

WikiHomeTopLink类似,为了层次上更清晰,采用了多重继承,在models.py中只定义ParentalKey外键,而在另一个文件中定义了RelatedLink模型,包含的字段有链接文本和具体链接,只是具体链接可能为外链、某个页面或某个文档,占用了三个字段,此外还利用@property装饰器为该模型添加了link属性,来返回它的具体链接,这样在模板中就可以使用.link调用。

综上,models.py的内容因篇幅有限,代码已上传至社区小密圈,可点击阅读原文下载。在models.py旁新建umodels.py文件供models.py引用:

它有一系列现成的layouts供我们使用,选择最适合本次主页的样式,查看源码可以得到详细的信息,在这里,为了简便,我们直接使用了该layout的额外样式表的链接(最好处理为本地的css样式文件,使用Django的static标签引用)。

对于模板来说,它对应的页面模型处于它的上下文环境,在模板中可以调用到该页面模型中的所有元素(使用Django的模板语言)。我们要按照页面排版将元素填充进去。

修改wiki_home.html中内容(因篇幅有限,代码已上传至小密圈,点击阅读原文下载即可)。

实际上,它还是创建了一个Django模型,只包含了一个富文本字段,但是利用Wagtail提供的register_snippet装饰器我们可以简便地将其注册到管理界面,以便在管理界面修改。但是,还不能在模板中调用它,我们需要将它注册到Django的tag标签系统中,在wiki目录下新建templatetags文件夹,在该文件夹下新建wiki_tags.py文件,添加如下内容。同样,借助简单的装饰器注册了该模板标签,且与wiki/tags/footer.html片段模板绑定,并提供footer_text作为上下文。

然后就该创建对应的片段模板文件了。与上面代码中绑定的html文件路径对应,在wikiapp目录下新建templates\wiki\tags\footer.html文件,添加如下内容:

好了,主页的所有代码部分都结束了。让我们尝试运行。在项目根目录下执行:

登录管理界面::8000/admin/

点击红圈部分来到如下图页面,删除默认页面。

确认删除后,选择在根目录下新建页面

这时便来到我们的创建的WikiHome模型的页面元素填写界面,依次填写后按红圈处Publish提交。

页面创建好后,我们需要将其挂载到站点上来正常显示,点击下图红框创建站点

如下图创建并保存,Root Page选择新创建的页面。

最终,访问:8000/便可以看到页面效果。

不过细心的朋友可能会发现页脚还是空的,我们还需要在管理界面设置下页脚,点击snippets栏,并点击红圈

创建并保存

大功告成,我们的页脚也完善了,整个首页的制作就此完成。全部代码与样例页面所在数据库在github上,wagtail-tutorial-1,可直接运行,管理员账号lake,密码123,也可另创管理员。

本站内容来自用户投稿,如果侵犯了您的权利,请与我们联系删除。联系邮箱:835971066@qq.com

本文链接:http://news.xiuzhanwang.com/post/1379.html

发表评论

评论列表

还没有评论,快来说点什么吧~

友情链接: