发布于2025年12月5日12月5日 ### Esprima 是什么\r Esprima 是一个遵循ECMAScript 的JavaScript 解释器,目前支持最高ES6 的语法解析。 \r 通过Esprima提供的API可以将一段JavaScript代码解析为节点语法树。 \r \r #### 源代码\r ```javascript=\r 变量名称='xray'\r ````\r #### 语法树\r ```JSON\r [\r {\r \'类型\': \'变量声明\',\r \'声明\': [\r {\r \'类型\': \'变量声明符\',\r \'id\': {\r \'类型\': \'标识符\',\r \'名称\': \'名称\'\r },\r \'初始化\': {\r \'类型\': \'文字\',\r \'值\': \'xray\',\r \'原始\': \''xray'\'\r }\r }\r ],\r \'kind\': \'var\'\r }\r ]\r ````\r \r ### 为什么要对JS进行语义分析\r 之前挖掘过一些DOM-XSS,感觉手动审计js来挖掘漏洞更准确,但效率较低。 \r 我也尝试过使用chrome扩展来做一些hook分析,但是结果并不理想。 \r \r 总结了一些之前发现的XSS漏洞。大多数漏洞中,漏洞点出现在“赋值表达式”和“危险函数调用”中。 \r \r **赋值表达式引起的DOM-XSS**\r ```javascript=\r //标签\r document.getElementById('id').innerHTML=代码\r //属性\r document.getElementById('id').src=代码\r //伪协议\r window.location.href=代码\r ````\r \r **由危险函数调用引起的XSS**\r ```javascript=\r //评估\r 评估(代码)\r //文档.write\r 文档.write(代码)\r ````\r 上面两类,一看就很明显是代码有问题。 \r 然而,随着前端技术的不断进步,自从引入了webpack技术之后,审计JS就变得不再那么容易了。代码压缩、变量名混淆等都让代码审计很头疼,比如下面的代码\r \r #### 源代码\r ```javascript=\r 函数getQueryString(名称) {\r var reg=new RegExp(\'(^|)\' + name + \'=([^]*)(|$)\', \'i\');\r var r=window.location.search.substr(1).match(reg);\r if (r !=null) return unescape(r[2]);\r 返回空值;\r }\r \r var url=getQueryString(\'url\')\r window.location.href=url\r ````\r #### 网页包\r ```javascript=\r !函数(e) {\r var n={};\r \r 函数t(r) {\r if (n[r]) 返回n[r].exports;\r var o=n[r]={i: r,l:1,exports: {}};\r 返回e[r].call(o.exports, o, o.exports, t), o.l=!0, o.exports\r }\r \r t.m=e,t.c=n,t.d=函数(e, n, r) {\r 至(e, n) || Object.defineProperty(e, n, {enumerable:0, get: r})\r }, t.r=函数(e) {\r \'未定义\' !=typeof Symbol Symbol.toStringTag Object.defineProperty(e, Symbol.toStringTag, {value: \'Module\'}), Object.defineProperty(e, \'__esModule\', {value:0})\r }, t.t=函数(e, n) {\r if (1 n (e=t(e)), 8 n) 返回e;\r if (4 n \'object\'==typeof e e e.__esModule) return e;\r var r=Object.create(null);\r if (t.r(r), Object.defineProperty(r, \'默认\', {\r 可枚举:0,\r 值: e\r }), 2 n \'string\' !=typeof e) for (var o in e) t.d(r, o, function (n) {\r 返回e[n]\r }.bind(null, o));\r 返回r\r }, t.n=函数(e) {\r var n=e e.__esModule ?函数() {\r 返回e.default\r } : 函数() {\r 返回e\r };\r 返回t.d(n, \'a\', n), n\r }, t.o=函数(e, n) {\r 返回Object.prototype.hasOwnProperty.call(e, n)\r }, t.p=\'\', t(t.s=0)\r }([函数(e, n) {\r var t, r,\r o=(t=new RegExp(\'(^|)\' + \'url\' + \'=([^]*)(|$)\', \'i\'), null !=(r=window.location.search.substr(1).match(t)) ? unescape(r[2]) : null);\r window.location.href=o\r }]);\r ````\r 一个DOM-XSS demo打包后,不仅对于初学者,对于常年看JS的人来说都是一件非常麻烦的事情。如果加上一些逻辑判断和加密算法,代码审计就变得更加困难。 \r \r ### 这个解决方案可行吗? \r \r 如果我们静下心来仔细阅读代码,我们会发现,无论代码多么混乱,都是有源头的。最终触发XSS的核心代码就是这一段:\r ````\r var t, r,\r o=(t=new RegExp(\'(^|)\' + \'url\' + \'=([^]*)(|$)\', \'i\'), null !=(r=window.location.search.substr(1).match(t)) ? unescape(r[2]) : null);\r window.location.href=o\r ````\r \r 核心是变量`o`的值,可以追溯到`location.search.substr(1).match(t)t`,可以拆分为:\r \r \r 最终触发点是:\r `window.location.href=o`\r \r \r \r \r 我们只需要解析语法树,最终确定o来自哪里。由于condition中的条件很难判断(我的偏好),所以暂时不考虑。你只需要知道这个参数来自URL即可。 \r \r 于是借助MDN Web文档,查询了一些字符串处理的方法:\r ````\r 'split'、'replace'、'concat'、'match'、'matchAll'、'sub'、'substr'、'substring'、'toLocaleLowerCase'、'toLocaleUpperCase'、'tirm'、'toLowerCase'、'toUpperCase'、'toString'、'trimEnd'、'trimStart'、 'valueOf', '原始'\r ````\r 如果字符串的属性是这些方法之一,则说明大多数情况下该字符串仍在可控范围内。 \r 如果字符串的属性是length,那么就不需要继续跟踪后面的表达式了。 \r \r 那么通过上面这个简单的例子,我们可以确认通过语法树解析DOM-XSS是可行的。 \r \r ### 该计划的难点和陷阱是什么?\r \r 该方法当然是可行的,但是我在尝试解析它时遇到了哪些陷阱? \r \r - 剧本爽一会,重建火葬场\r \r 我第一次使用Esprima的时候,直接拉了一段代码进去,尝试解析。解析了一段时间,发现js表达式比我想象的要复杂很多。 \r \r 分配操作可能需要考虑许多因素。 \r `var a=b;`\r \r \r \r \r - 茴香豆的“茴香”一词有多少种写法? \r \r js的语法非常灵活,这也使得解析变得非常困难。我们常见的赋值表达式大概包括以下几种\r ````\r //AssignmentExpression表达式\r a=父.顶部.窗口\r b=父.顶部.窗口.位置\r c=父.顶部.窗口.位置.href\r d=父.顶部.窗口.文档\r e=parent.top.window.document.cookie\r f=父.top.window.document.URL\r g=窗口.名称\r h=父.顶部.窗口.名称\r 我=位置\r j=窗口\r k=父级\r l=文档\r m=文档['cookie']\r n=top.parent.window.parent['名称']\r o=top.parent.window['父级']['名称']\r p='位置'\r q=窗口[p]\r ````\r 有些对象是可控的,例如window.location.href和window.name,有些对象是部分可控的,例如document.URL和document.cookie,而document.domain则不可控。我们要做的就是区分哪些对象是可控的,哪些是不可控的。 \r \r ````\r b - 位置\r c - 位置.href\r 电子文档.cookie\r f - 文档.cookie\r g - 窗口名称\r h - 窗口名称\r i-位置\r m - 文档.cookie\r n - 窗口名称\r o - 窗口名称\r q - 位置\r ````\r 经过分析,我们得到可控值。当表达式左边是单个变量名时,就比较简单。如果表达式的形式如下\r ````\r a.b.c=d.e.f\r ````\r 需要分割出每个变量的原始值和范围,分析对象是否可控。 \r \r 希望有兴趣的同学可以一起研究一下JS语法树,让简单的、表层的DOM-XSS更容易挖掘,从而可以有更多的精力和时间去学习更深层次的语法特征。
创建帐户或登录后发表意见