本文分析了w3af的xss插件,旨在理解w3af检测xss漏洞的原理。
一、预备知识
1.XSS
当应用程序收到含有不可信的数据,在没有进行适当的验证和转义的情况下,就将它发送给一个网页浏览器,这就会产生跨站脚本攻击(简称XSS)。XSS允许攻击者在受害者的浏览器上执行脚本,从而劫持用户会话、危害网站、或者将用户转向至恶意网站。(参考:OWASP TOP 10 中文v1.2)
2.audit
对于审计(audit)的理解,审计就是找漏洞。代码审计是一种白盒测试方法,从源代码中发现漏洞;w3af中使用的audit,是一种黑盒测试方法,从外部寻找漏洞。
3.payload
有效载荷(payload)通常指的恶意代码执行破坏性操作的部分,在w3af中指的是包含特殊字符的测试字符串,用来检验这些特殊字符是否被过滤等,并非执行恶意操作。
4.fuzzing
模糊测试(fuzzing, fuzz testing)是一种基于缺陷注入的自动软件测试技术。通过编写fuzzer工具向目标程序提供某种形式的输入并观察其响应来发现问题,这种输入可以是完全随机的或精心构造的,为了寻找软件缺陷,主要是构造畸形数据。Fuzzer工具能很好的检测出可能导致程序崩溃的问题,如缓冲区溢出、跨站点脚本、拒绝服务攻击、格式漏洞和SQL注入。w3af中有一个FuzzableRequest类,该类灵活表示了一个http 请求包。
5.mutant
在w3af中mutant类是对FuzzableRequest类的包装,然后在mutant中添加设置不同的payload,用这些mutant扫描测试网站的漏洞。
二、XSS插件
以w3af\plugins\audit\xss.py的xss.audit函数为主线,深入分析了w3af扫描xss漏洞的原理。
1.xss.payloads
PAYLOADS = ['RANDOMIZE',
'RANDOMIZE/*',
'RANDOMIZE"RANDOMIZE',
"RANDOMIZE'RANDOMIZE",
"RANDOMIZE`RANDOMIZE",
"RANDOMIZE ="]
PAYLOADS中起作用的只是里面特殊字符:,/*,",',`,=;
RANDOMIZE只是为了在http响应报文中定位到特殊字符的位置,在些它只是一个占位用的标识字符串,在使用时会随机生成一个字符串,替换RANDOMIZE;
2.xss.audit(freq,orig_response)
orig_response在函数中没有使用,可能是预留的一个参数;
freq是FuzzableRequest类对象,表示一个模糊http请求包;
功能:检测一个URL的XSS漏洞,被检测的URL在freq中;
执行过程如下:
- 创建一组freq的变体fake_mutants;
- 对于一个fake_mutant,扫描(持久型)XSS漏洞;
- 首先扫描简单的XSS漏洞(所有字符不经过编码和过滤就直接反射回来),详细过程见_identify_trival_xss(),如果找到XSS漏洞,直接返回;
- 然后分析fake_mutant,扫描检测反射型XSS漏洞,详细过程见_search_xss();
3.xss._identify_trival_xss(mutant)
功能:识别简单的XSS漏洞:所有字符不经过编码和过滤就直接反射回来;
- 将所有的特殊字符(,/*,",',`,=)放在一块,组成一个payload;
- 将payload添加到mutant中;
- 将mutant发到服务器,返回一个HTTPResponse;
- 如果payload在reponse.body中(也就是说所有特殊字符没有被过滤或者编码就直接反射回来),那就是存在xss漏洞;
- _report_vuln()创建一个Vuln,并且mutant等存储到KB中;
4.xss._search_xss(mutant)
_search_xss()执行流程
- 0.用xss.PAYLOADS构造http请求包变体列表mutant_list
- 1.发送mutant,得到一个HttpResponse
- 2.调用body=HttpResponse.get_body()
- 调用HttpResponse._charset_handling(),返回unicode(decoded) body 和used charset
- http body可能就是标签内的正文,或者是整个网页?
- 3.根据mutant得到发送的payload
- 4.调用get_context_iter(body, payload)函数
- 1). chunks = body.split(payload)
- 2). data=''
- 3). 遍历data从chunks[0]迭加到chunks[-1](倒数第2个)
- 4). byte_chunk = ByteChunk(data)
- ByteChunk数据成员如下
- self.attributes=dict()
- self.data = data
- 5). 遍历get_contexts()
- [HtmlAttrSingleQuote(), HtmlAttrDoubleQuote(), HtmlAttrBackticks(), HtmlAttr(), HtmlTag(), HtmlText(), HtmlComment(), ScriptMultiComment(), ScriptLineComment(),ScriptSingleQuote(), ScriptDoubleQuote(), ScriptText(),StyleText(), StyleComment(), StyleSingleQuote(),StyleDoubleQuote()],具体定义见下一节Context
- 6). 判断context.match(byte_chunk)
- 如果匹配,1.context.save(data);2.迭代返回context
- 5. 如果context.is_executable()或者context.can_break()为True,则判定为XSS漏洞
- 6._report_vuln()创建一个Vuln,并且存储到KB中
- 7.注释:Context类族,match(), is_executable(), can_break()分析见下一节Context
5.Context
- 数据成员name, data
- 下面是类继承关系,大写字母开头的
- HtmlContext
- HtmlTag
- can_break
- 如果' ','>'在payload中,就判定存在xss漏洞
- HtmlText
- can_break
- 如果'<'在payload中,就判定存在xss漏洞
- HtmlComment
- can_break
- 如果'-','>','<'在payload中,就判定存在xss漏洞
- HtmlAttr
- HtmlAttrQuote
- HtmlAttrSingleQuote
- - HtmlAttrDoubleQuote
- HtmlAttrDoubleQuote2Script
- HtmlAttrDoubleQuote2ScriptText,它的父类还有一个ScriptText
- HtmlAttrBackticks
- 上面是HtmlAttrQuote的子类,下面是它的成员
- self.quote_character
- 有三个',",`(backtick,可能显示不出来), (但是没找到赋值的地方)
- can_break()
- 如果quote_character在payload中,就判定为xss漏洞
- is_executable()
- 该函数先将self.data变成小写,删除空格判断self.data是否以下面的字符串结尾,如果是,就返回True['href', 'src','onclick', 'ondblclick', 'onmousedown', 'onmousemove','onmouseout', 'onmouseover', 'onmouseup', 'onchange', 'onfocus', 'onblur', 'onscroll', 'onselect', 'onsubmit', 'onkeydown', 'onkeypress', 'onkeyup', 'onload', 'onunload'] +'=' +self.quote_character. 比如data的尾部是onclick=" ,则返回True
- match(byte_chunk)
- 有两个装饰器,其实是返回inside_html(not_html_comment(_match(byte_chunk)))
- _match(byte_chunk)
- data= byte_chunk.nhtml
- - normalize_html(data)
- (data.replace("\\'",'')).replace('\\"','')
- data = StringIO(data)
- 然后一个字符一个字符的处理data
- 1.删除闭合的',",`
- 2.处理标签
- ,分别替换为<, >。注意不处理注释,即在注释中,返回False
- inside_html()