当 < 没有转义但如果后面跟着一个字符也可以删除时,XSS 是否可能?

信息安全 xss
2021-08-30 21:26:25

我发现它<没有被编码为&lt;. 但是,如果后跟除空格之外的任何内容,则会将其删除。例如,<a将被删除但不会< a>(注意空格),这是没有用的。

<字符外,没有限制,也没有编码字符。

这是反映它的代码:

<span>USER INPUT</span>

这是页面的内容类型:

text/html; charset=utf-8

以及输入请求的内容类型:

application/x-www-form-urlencoded; charset=UTF-8 

其他具体信息:

Server: nginx
X-Powered-By: PHP/5.5.18-1~dotdeb.1

这种方法容易受到 XSS 攻击吗?

4个回答

不,在 HTML 上下文中,如果不允许左括号后的字母,则不能注入新标签。尽管如此,这种过滤技术还是有不必要的风险。

Web 浏览器的 HTML 解析器将代码解析为状态机要了解您的选择,请查看HTML 语法规范和可能的状态转换。

您的注入点恰好处于数据状态(这是“默认”状态,在任何标签之外):

8.2.4.1 Data state

Consume the next input character:

U+0026 AMPERSAND (&)
    Switch to the character reference in data state.
"<" (U+003C)
    Switch to the tag open state.
U+0000 NULL
    Parse error. Emit the current input character as a character token.
EOF
    Emit an end-of-file token.
Anything else
    Emit the current input character as a character token.

对于 XSS,唯一有趣的延续是打开标签<并切换到标签打开状态

8.2.4.8 Tag open state

Consume the next input character:

"!" (U+0021)
    Switch to the markup declaration open state.
"/" (U+002F)
    Switch to the end tag open state.
Uppercase ASCII letter
    Create a new start tag token, set its tag name to the lowercase version of the current input character (add 0x0020 to the character's code point), then switch to the tag name state. (Don't emit the token yet; further details will be filled in before it is emitted.)
Lowercase ASCII letter
    Create a new start tag token, set its tag name to the current input character, then switch to the tag name state. (Don't emit the token yet; further details will be filled in before it is emitted.)
"?" (U+003F)
    Parse error. Switch to the bogus comment state.
Anything else
    Parse error. Switch to the data state. Emit a U+003C LESS-THAN SIGN character token. Reconsume the current input character. 

在这里,您的选项是a-zA-Z您还不清楚特殊字符是否也被列入黑名单。但即使他们不是,你也不走运:!/?

  • <!只能得到一个注释 ( <!--)、一个文档类型声明 ( <!DOCTYPE) 或一个 CDATA 部分 ( <![CDATA)。这些不是真正的 DOM 节点,因此它们对 XSS 毫无用处。(例如,您将无法将事件处理程序附加到评论。)
  • <? 在 XML 中可能很有趣,但在 HTML 中被视为注释。
  • </ 只会让你关闭标签。

您可能已经注意到该规范也不允许任何填充字符,例如空格、制表符、换行符或控制字符。

如果您想更深入地挖掘并验证实现,您可以随时查看源代码。例如,此摘录是Mozilla Firefox 中使用的 HTML5 标记器的一部分。如您所见,标签打开状态非常符合规范:

case NS_HTML5TOKENIZER_TAG_OPEN: {
  for (; ; ) {
    if (++pos == endPos) {
      NS_HTML5_BREAK(stateloop);
    }
    c = checkChar(buf, pos);
    if (c >= 'A' && c <= 'Z') {
      endTag = false;
      clearStrBufAndAppend((char16_t) (c + 0x20));
      state = P::transition(mViewSource, NS_HTML5TOKENIZER_TAG_NAME, reconsume, pos);
      NS_HTML5_BREAK(tagopenloop);
    } else if (c >= 'a' && c <= 'z') {
      endTag = false;
      clearStrBufAndAppend(c);
      state = P::transition(mViewSource, NS_HTML5TOKENIZER_TAG_NAME, reconsume, pos);
      NS_HTML5_BREAK(tagopenloop);
    }
    switch(c) {
      case '!': {
        state = P::transition(mViewSource, NS_HTML5TOKENIZER_MARKUP_DECLARATION_OPEN, reconsume, pos);
        NS_HTML5_CONTINUE(stateloop);
      }
      case '/': {
        state = P::transition(mViewSource, NS_HTML5TOKENIZER_CLOSE_TAG_OPEN, reconsume, pos);
        NS_HTML5_CONTINUE(stateloop);
      }
      case '\?': {
        if (viewingXmlSource) {
          state = P::transition(mViewSource, NS_HTML5TOKENIZER_PROCESSING_INSTRUCTION, reconsume, pos);
          NS_HTML5_CONTINUE(stateloop);
        }
        if (P::reportErrors) {
          errProcessingInstruction();
        }
        clearStrBufAndAppend(c);
        state = P::transition(mViewSource, NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
        NS_HTML5_CONTINUE(stateloop);
      }
      case '>': {
        if (P::reportErrors) {
          errLtGt();
        }
        tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 2);
        cstart = pos + 1;
        state = P::transition(mViewSource, NS_HTML5TOKENIZER_DATA, reconsume, pos);
        NS_HTML5_CONTINUE(stateloop);
      }
      default: {
        if (P::reportErrors) {
          errBadCharAfterLt(c);
        }
        tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
        cstart = pos;
        reconsume = true;
        state = P::transition(mViewSource, NS_HTML5TOKENIZER_DATA, reconsume, pos);
        NS_HTML5_CONTINUE(stateloop);
      }
    }
  }
  tagopenloop_end: ;
}

因此,根据 HTML 规范,您描述的 XSS 过滤器似乎是安全的。冰很薄,很硬。你永远不知道,如果某个供应商提出了一个仍然可以启用漏洞利用的古怪实现。(微软,我在看着你!)

因此,正确的 XSS 保护是简单地转义括号

非常不可能。我能看到的唯一向量是一个字符编码错误,其中后端语言(例如 PHP)被配置为使用与 UTF-8 不同的字符编码,并且在输出字符串时它不支持编码。

也就是说,我想说在这种情况下几乎肯定不可能获得 XSS。但是,您可以在标签内的任何位置获取 XSS(例如,回显到属性中)。

在这种特殊情况下,您将输入作为HTML 代码注入(因为它位于跨标签之间,并且内容类型是 text/html)。所以,我能想到的注入 JavaScript 代码的唯一方法是使用 HTML 标签。鉴于服务器的响应方式,在这种特殊情况下您不太可能注入 JavaScript。

您有 2 个选择:首先,您可以尝试查找是否可以找到您已经在 HTML 标记中的另一个位置(如 @Polynomial 所述)。其次,您可以在页面上找到一个位置,该输入作为 JavaScript 注入(例如作为事件处理程序的值,在标签内等),而不是 HTML。

如果您能够找到第二个条件(将输入作为 JS 注入)并且您处于服务器试图将某些字符列入黑名单的类似情况,您可以尝试使用JSF##k 之类的东西来绕过特定的文件管理器/黑名单。JSF##k 是一种基于 JavaScript 原子部分的深奥且具有教育意义的编程风格。它只使用六个不同的字符来编写和执行代码。看看这篇文章以获取更多信息。

首先也是最重要的 XSS 完全依赖于显示输出的上下文。通过同一个过滤器强制所有输入永远不会一直有效 - 并且将要求开发人员和渗透测试人员寻找所有注入。内容安全策略将有助于防止所有类型的 XSS,即使应用程序的输出环境受到破坏。

考虑到这些任意限制,XSS 有多种可能的方式。首先想到的是基于 DOM 的 XSS事件处理程序注入反射型 XSS 也可以在以下情况下工作:

<script>
var a="$xss";
</script>

概念证明:"+alert(document.cookie)+"

或者,如果它是一个位置:

<a href="$xss">click me</a>
<iframe src="$xss"/>

概念证明:javascript:alert(1)