в Positive Technologies • AppSec- и CS-исследователь в области формальных методов анализа и защиты приложений • Активный участник и организатор Positive Development User Group [email protected] :~$ whoami
и всем нравился» • «Нужно, чтобы всё было, как в других модулях, но только для JavaScript» • «Обязательно обеспечьте поддержку Node.js» • «А ещё фронтенд было бы неплохо. React.js – обязательно, ну и Angular тоже. Наверное» Базовые требования к исследованию
отдельные фрагменты кода в песочнице • Доказательство существования уязвимостей через построение векторов атак • Генерация информации для ручного доказательства подозрений на уязвимость Чуть более формально
Microsoft (https://github.com/Microsoft/ChakraCore) • написан на С++, кроссплатформенный (Windows, Linux, OS X), поддерживает основные архитектуры: x86, x64, arm; • регистровая архитектура (данные хранятся в регистрах); • использование аппаратного обеспечения по максимуму, потребление минимального количества ресурсов; • оптимизация быстрого запуска и выполнения JS кода; Microsoft ChakraCore
BrEq_A - branch if '==' (general equals) BrNeq_A - branch if '!=' (not general equals) BrGt_A - branch if '>' BrSrEq_A - branch if '===' (strict equals) BeginSwitch - the start of a switch statement EndSwitch - equivalent to Br OpCode Case - equivalent to Branch if '===' (strict equals) Call - call (direct) registered function Throw - throw exception Ret - return from function Object operations LdFld - load from object instance's direct field StFld - store into object instance's direct field InitFld - declare a property with an initial value DeleteFld - remove a property Instancing operations NewScObject - create new object instance NewScArray - create new array instance NewScFunc - create new function instance NewRegEx - create a new RegEx expression
!C2) or (C1 and C2) C1 and !C2 False Line Br C2 Сохраняем значения и их условия Переписываем значения, используя условие С1 and !C2 Мержим текущие значения для условия перехода C1 and C2 C1 and C2
перехода с их условиями AND условие перехода Fi-нода Множество значений до прыжка и после различаются Множество значений до прыжка и после совпадают Значения и их условия уже верны Значения и их условия на момент слияния
= {name: "foo", one: 1, two: 2}; //Создание еще одного нового объекта var bar = {two: "two", three: 3}; bar.__proto__ = foo; // foo теперь является прототипом для bar //Если теперь мы попробуем получить доступ к полям foo из bar, то всё получится bar.one // Равно 1 //Свои поля тоже доступны bar.three // Равно 3 //Собственные поля выше по приоритету полей прототипов bar.two; // Равняется "two"
среды выполнения JavaScript трёх типов: • Объекты • Массивы • Функции • Каждый entity-литерал хранит ссылку на родительскую сущность, скоупу которой он принадлежит • Кроме корневого entity-литерала «global» Entity-литералы
var express = require('express'); var app = express(); app.get('/', function (req, res) { // это оно и есть res.send('Hello World!'); }); app.listen(3000, function () { console.log('Example app listening on port 3000!'); }); Динамика во всём
var c = a + b; var o = {}; o[c.toUpperCase()] = function () { return http_request_params["param1"]; } http_response.send(o.XSS()); // XSS by param1 Общий случай: динамический код • В JavaScript динамическим является практически всё: • иерархия, структура и состояния объектов; • граф связывания выражений; • структура интерпретируемых модулей; • граф вызовов; • и т.д. • Чтобы проанализировать это в статике нужна… динамика!
symbolic-выражений в реальной среде выполнения: • JavaScript runtime • Node.js runtime • Интерпретатор pure-семантики булевых выражений • Основная стратегия: «в любой непонятной ситуации, вычисляй всё, что можно вычислить (но не больше)». Частичное вычисление
var c = a + b; var o = {}; o[c.toUpperCase()] = function () { return http_request_params["param1"]; } http_response.send(o.XSS()); // XSS by param1 Процесс частичного вычисления "x" – литерал "ss" – литерал Evaluate("x" + "ss") === "xss" {} – пустой объект Evaluate("xss".toUpperCase()) === "XSS" http_request_params["param1"] – неизвестная o.XSS – известная функция, «проваливаемся» в неё и получаем в ответе: http_request_params["param1"]
JavaScript могут также быть истинноватыми (truthy) и ложноватыми (falsy) – буленоватыми. Буленоватым называют выражение, не принадлежащее множеству { true, false }, но приводимое к одному из этих значений неявно, например, в условных операторах. Может ли данный код вывести в консоль значение 'false': if (x) { console.log(x.valueOf()) } ? Буленоватая логика (1/3)
JavaScript могут также быть истинноватыми (truthy) и ложноватыми (falsy) – буленоватыми. Буленоватым называют выражение, не принадлежащее множеству { true, false }, но приводимое к одному из этих значений неявно, например, в условных операторах. Может ли данный код вывести в консоль значение 'false': if (x) { console.log(x.valueOf()) } ? Буленоватая логика (2/3)
JavaScript могут также быть истинноватыми (truthy) и ложноватыми (falsy) – буленоватыми. Буленоватым называют выражение, не принадлежащее множеству { true, false }, но приводимое к одному из этих значений неявно, например, в условных операторах. Может ли данный код вывести в консоль значение 'false': if (x) { console.log(x.valueOf()) } ? Буленоватая логика (3/3)
JSBoolean Значения: JSTrue JSFalse Операции: JSAnd JSOr JSNot Операторы JS Операции PureAnd PureOr PureNot ToPureBoolean Выражение типа PureBoolean не может содержаться в выражении любого типа JavaScript (но не наоборот, т.к. есть ToPureBoolean)!
Решение уравнения в SMT-солвере Применение «ad-hoc» теорий Преобразование модели решения в symbolic-представление Microsoft Z3 CVC4 SMT via Automata.NET Equality assertions Fixed points Invertible functions Отображение модели решения на исходное уравнение Частичное вычисление узлов уравнения Решены новые узлы? Преобразования в семантике pure boolean Выделение доп.условий Возврат решения да нет