Универсальный сигнатурный анализ кода на c#, java, php

24

Upload: ivan-kochurkin

Post on 12-Apr-2017

306 views

Category:

Software


3 download

TRANSCRIPT

Page 1: Универсальный сигнатурный анализ кода на C#, Java, PHP
Page 2: Универсальный сигнатурный анализ кода на C#, Java, PHP

Универсальный сигнатурный анализ кода

на C#, Java, PHPКочуркин Иван, Positive Technologies

Page 3: Универсальный сигнатурный анализ кода на C#, Java, PHP

Почему сигнатурный анализ кода?–Проще в разработке, поддержке и тестировании.–Масштабируемость на другие языки.–Обработка файлов с синтаксическими ошибками.

Зачем унифицированное AST?–Не все паттерны описываются регулярными

выражениями.–Возможность реализации taint-анализатора

благодаря связанности AST.3

Ivan Kochurkin
При матчинге в чистом исходном коде. Кроме того, унифицированноть позволяет избежать "мусора" различных языков. Например, знак доллара перед переменной в PHP.
Ivan Kochurkin
В регулярном выражении очень неудобно пользовать бек-референсами.
Ivan Kochurkin
В динамическом анализе требуется не просто валидный синтаксис исходного кода, но и семантическая связанность кода, потому что происходит его выполнение. Сигнатурный анализ кода способен обрабатывать файлы даже с синтаксическими ошибками (например, отсутствующая точка с запятой).
Ivan Kochurkin
Благодаря разработанной архитектуре нашего ядра, для поддержки нового языка или диалекта потребуется разработать модуль конвертера и парсера. матчинг разрабатывать уже не придется.
Page 4: Универсальный сигнатурный анализ кода на C#, Java, PHP

Workflow

4

Ivan Kochurkin
В начале я опишу этапы работы нашего ядра паттерн-матчинга или PM. Это:1. Парсинг в зависимое от языка AST2. Преобразование в независимое от языка AST3. непосредственно паттерн-матчинг.Пока что наше ядро функционирует для языков C#, Java и PHP
Page 5: Универсальный сигнатурный анализ кода на C#, Java, PHP

Теория парсинга• Лексер + Парсер:

stmt -> e ; | if (e) stmt | for (e ; e ; e) | other ...

• Генераторы парсеров: ANTLR, Bison, Coco/R.void stmt() { switch (lookahead) { case if: match(if); match('('); match(e); match(')'); stmt(); break; ...

5

Ivan Kochurkin
Лексический анализ (сканирование) - группировка символов исходного кода в значащие последовательности, называющиеся лексемами. Токен - это лексама + ее тип. Например, в типичных ЯП токенами являются: Id, String, Integer, Keyword (If, While, For), скобки и т.д.Синтаксический анализ - создание древовидного представления или AST, которое описывает грамматическую структуру токенов. Листями в этом дереве являются токены, а узлами - тип операции. Например, IfElseStatement, арифметическое выражение и т.д.Семантический анализ - проверка на согласованность с определениями языка. Например, соблюдение согласованности типов.В данном примере токенами являются sp, =, 100, ;Для описания создания различных узлов AST дерева используются правила из потока токенов, объединение которых называют грамматикой. Здесь правилом является assign и stat.
Ivan Kochurkin
Преимущества и недостатки LL и LR парсинга (в LL нельзя использовать левую рекурсию, однако в ANTLR4 - можно, несмотря на то, что там LL (из-за улучшенного алгоритма)).
Page 6: Универсальный сигнатурный анализ кода на C#, Java, PHP

Проблематика парсинга

• Распознавание ключевых слов, как идентификаторов.id : ID | null | if | else | … /* другие keyword */ … ;

• Неоднозначные фразы.• Обработка комментариев.

// password = “forgotten password”;• Обработка ошибок парсинга.

6

Page 7: Универсальный сигнатурный анализ кода на C#, Java, PHP

Типичные ошибки парсинга

• Лексическая ошибкаclass # { int i; }

• Единичный лишний токен class T ; { int i; }

• Единичный отсутствующий токен class T { int f(x) { a = 3; } }

• Несколько лишних токенов (режим «паники») class T { int f(x) { a = 3 4 5; } }

• Отсутствующее альтернативное подправило class T { int ; }

7

Ivan Kochurkin
Лексическая ошибка.
Ivan Kochurkin
Удаление "лишнего" токена.
Ivan Kochurkin
Достраивание "отсутствующего" токена.
Ivan Kochurkin
Ожидание синхронизирующего токена ";". Игнорирование "4" и "5".
Ivan Kochurkin
Отсутствие альтернативы в правиле для матчинга фрагмента "int ;"
Ivan Kochurkin
Всего в ANTLR выделено 5 типов ошибок.
Page 8: Универсальный сигнатурный анализ кода на C#, Java, PHP

Виды грамматик• Регулярные

– Пример: an

• Контекстно-свободные– C#, Java (если проверка семантики после проверки синтаксиса). – Пример: anbn

• Контекстно-зависимые– C#, Java (если проверка семантики на этапе парсинга: T a = new T())– PHP конструкция Heredoc: $x = <<<EOL Hello world EOL;– Пример: anbnсn

8

Ivan Kochurkin
"Регулярное выражение не умеет считать".
Ivan Kochurkin
КС-грамматика может считать две вещи, а не три.
Ivan Kochurkin
На этом слайде представлены виды грамматик и различия между ними.
Page 9: Универсальный сигнатурный анализ кода на C#, Java, PHP

Грамматика PHP в ANTLR

• OpenSource: https://github.com/antlr/grammars-v4/tree/master/php

• Игнорирование «островков» HTML, CSS, JavaScript с помощью переключения Mode.

• Регистро-независмые ключевые слова.• Парсинг сложных контекстно-зависимых конструкций:

– Heredoc (многострочная строка).– Alternative Syntax.

9

Page 10: Универсальный сигнатурный анализ кода на C#, Java, PHP

Технологии парсинга кода в .NET

• Roslyn + Высокая производительность. + Продуманность и качественный парсинг, Microsoft. + Хорошая поддержка на GitHub. - Парсинг только C# и VisualBasic.

• ANTLR + Универсальность (много грамматик и различные runtime). + Хорошая поддержка на GitHub. - Плохая производительность в случае глубокой рекурсии. - Искусственный код парсера из-за автогенерации.

10

Ivan Kochurkin
Другого выбора особо и не было =)
Ivan Kochurkin
Зато этот код можно отлаживать. Для остальных языков.
Page 11: Универсальный сигнатурный анализ кода на C#, Java, PHP

Visitor vs Listener (Walker)

• Listener• Visitor

11

Ivan Kochurkin
Перейдем к этапу преобразования AST в унифицированный формат. Для обхода AST существует две стратегии: Visitor и Listener (или Walker в Roslyn). Разница между ними состоит в том, что в Visitor необходимо в ручную вызывать методы обхода потомков синтаксических узлов, а в Listener все узлы обходятся автоматически. Более того, в Visitor возможно возвращать значение при обходе. Таким образом, код преобразования дерева получается более функциональным и лаконичным (не использутеся глобальный стэк). На рисунке видим, что, например, при преобразовании языка PHP, ненужные узлы HTML и CSS не посещаются.
Page 12: Универсальный сигнатурный анализ кода на C#, Java, PHP

Преобразование AST

// ifStatement// : If parenthesis statement elseIfStatement* elseStatement?// | If parenthesis ':' innerStatementList elseIfColonStatement* elseColonStatement? EndIf ';'// ;public override AstNode VisitIfStatement(PHPParser.IfStatementContext context){ throw new NotImplementedException();}

• parenthesis – единичное выражение.• elseIfStatement* – массив из нуля или большего количества.• elseStatement? – если отсутствует, то null.• throw new ShouldNotBeVisitedException() – если узел не должен

посещаться.• VisitAndReturnNullIfError() – используется для коллекций.

12

Ivan Kochurkin
На этом слайде описан пример метода-визитора VisitIfStatement. В комментариях описано правило соответствующего посещающегося узла. AstNode - тип узла унифицированного дерева. Как понятно из описания, на этом этапе, весь код уже представлен в структурированном виде.
Page 13: Универсальный сигнатурный анализ кода на C#, Java, PHP

Структура типов унифицированного AST

13

Ivan Kochurkin
Все узлы являются наследником AstNode. Особое внимание стоит уделить Expression, Statement и Literal.Expression - обычное ариметическое выражение, вызов метода, обращение к члену объекта и тд.Literal - примитивное строковое, числовое или другое значение.Statement - выражения, заканчивающиеся точкой с запятой в типичных ЯП. Будет использоваться главным образом для тайнта.Узлы с приставкой Pattern - являются искусственными и предназначены для того, чтобы при обработке матчить не просто одно литеральное значение или выражение, а целый класс. Например, литерал по регулярному выражению или диапазон чисел.
Page 14: Универсальный сигнатурный анализ кода на C#, Java, PHP

Алгоритм сопоставления в AST

14

Ivan Kochurkin
На этом слайде описаны основные принципы сравнения узлов в AST. Паттерн представляет кусочек AST.При обходе унифицированного AST (оно обходится в глубину), каждый кусочек "пытается" наложится на кусочек AST текущего узла.В начале сравнивается тип узла. Если узел является конечным (терминальным), то существует 4 типа сравнений.1. Рекурсивное сравнение потомков.2. Сравнение простых литеральных типов (идентификатор, строки, числа).3. Сравнение расширенных литеральных типов (регулярные выражения, диапазоны). Комментарии входят в эту группу.4. Сравнение сложных расширенны типов (выражения, последовательность Statement).Благодаря разработанной технологии матчинга (представление типа узла в виде Enum (то есть целого числа), удалось достигнуть высокой производительности при сравнительно небольшом количестве кода (метод Compare реализован для базового класса и терминалов).Стоит отметить, что сравниваются узлы, а не строки.
Page 15: Универсальный сигнатурный анализ кода на C#, Java, PHP

Язык описания паттернов

• Hardcode+ Отсутствие какого-либо парсинга.- Громоздкий и нечитаемый синтаксис.- Невозможность добавления своих паттернов без перекомпиляции.

• JSON (YAML, XML)+ Простая реализация. - Громоздкий и нечитаемый синтаксис.

• DSL.+ Краткий и лаконичный синтаксис.- Необходимость разрабатывать грамматику и конвертер для нее.

15

Ivan Kochurkin
Все паттерны или "кусочки дерева" необходимо загружать в программу каким-либо образом. И на этом слайде описаны подходы для описания паттернов.Во-первых, паттерны можно записывать в ручную прямо в коде (что делается у нас в других ядрах).Для этого не требуется разрабатывать какой-либо парсер. Конечно, данный подход не подходит для записи паттернов "не программистом", однако может использовать и использовался для написания юнит-тестов. Кроме того, для внесения новых паттернов, требуется перекомпиляция всей программы.Во-вторых, паттерны можно представлять в распространненом универсальном формате передачи данных, JSON. При этом подходе паттерны можно загружать извне, однако при этом синтаксис будет громоздким и неприемлимым для редактирования человеком. Однако данный формат можно использовать для сериализации древесных структур (сохранения и загрузки с диска).И, наконец, третий подход заключается в разарботке специального предметно-ориентированного языка (или DSL), который можно было бы легко редактировать, который был бы лаконичным, но который при этом был бы достаточно выразительным для описания существующих или будущих паттернов. Недостатком такого подхода является разработка синтаксиса, а потом и парсера для такого языка.
Page 16: Универсальный сигнатурный анализ кода на C#, Java, PHP

Целесообразность DSL?

16

Ivan Kochurkin
Часто задают вопрос, а нужен ли наш собственный DSL, если существуют регулярные выражения? Зачем еще один 15 формат?
Page 17: Универсальный сигнатурный анализ кода на C#, Java, PHP

DSL ≠ Regex

DSL – это регулярные выражение + синтаксические конструкции.

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

"private_key_bits" = <[0..2047]>

• Regex

• DSL - не нужно больше думать о проблемах пробелах.

17

Ivan Kochurkin
Ответ: да, нужен! DSL - это больше, чем регулярные выражения, это регулярные выражения, смешанные с синтаксическими конструкциями, похожие на конструкции из стандартных языков программирования. С помощью регулярок тяжело записывать длинные и хоть сколько-нибудь сложные выражения. Ну и у DSL название говорит само за себя: это язык, предназначенный для конкретной предметной области, он внутренний и не будет использовать в качестве какого-либо стандарта.
Page 18: Универсальный сигнатурный анализ кода на C#, Java, PHP

Синтаксические конструкции в DSL

• expr(args) – вызов метода.• expr.Id – обращение к члену объекта (поле, метод).• Id expr = expr – инициализация переменной.• expr + expr – конкатенация.• new Id(args) – создание объекта.• expr[expr] – обращение по индексу или ключу.

18

Ivan Kochurkin
Можно руководствоваться этим: https://wiki.ptsecurity.com/display/AI/DSL
Ivan Kochurkin
Что же представляют собой регулярные выражения с синтаксическими конструкциями?Прежде расскажу о синтаксических конструкциях: это обычные операции, заимствованные из типичных языков программирования: типа вызов метода, обращения к члену объекта, инициализации переменной .... На этом слайде в эти конструкции вставлены такие аббревеатуры, как expr, args и Id.
Page 19: Универсальный сигнатурный анализ кода на C#, Java, PHP

Синтаксис DSL• <[]> - оператор расширенного выражения (<[md5|sha1]> или <[0..2048]>)• # или <[expr]> - любое Expression• ... или <[args]> - произвольное количество любых аргументов• (expr.)?expr – эквивалентно expr.expr или просто expr• ~expr – отрицание условия.• expr (<[||]> expr)* - объединение нескольких условий (ИЛИ).• Comment: “regex” – поиск по комментариям.

Типы значений в DSL Id, String, Int, Bool, Null

19

Ivan Kochurkin
Прежде всего expr, или expression может являться обыкновенным примитивным значением типа Id, String, Int, Bool, Null. Однако если нужно матчить не просто одну строку, идентификатор или число, а строку, идентификатор по регулярному выражению или диапазон числе, то необходимо воспользоваться так называемым расширенным выражением, который обозначается так <[]>.Также в нашем DSL для многих частоиспользуемых конструкций предусмотрен сокращенный синтаксис, называемый "синтаксическим сахаром". Например, решетка обозначает любой Expression, три точки - произвольное количество любых аргументов.Интересными дополнительными конструкциями являются отрицание условия (например, когда требуется, чтобы в качестве аргумента указывалась не строка) и объединение нескольких условий, т.е. логическое ИЛИ для паттернов. Также была введена конструкция для поиска именно по комментариям, а не по коду: Comment.Стоит отметить, что синтаксис нашего DSL все еще в процессе фидбека, разработки и упрощения.
Page 20: Универсальный сигнатурный анализ кода на C#, Java, PHP

DSL примеры

• Hardcoded Password (All: C#, Java, PHP): (#.)?<[(?i)password(?-i)]> = <["\w*" || null]>

• Weak Random Number Generator (C#, Java):new Random(...)

• Debug Information Leak (PHP)Configure.<[(?i)^write$(?-i)]>("debug",<[1..9]>)

• Insecure SSL connection (Java)new AllowAllHostnameVerifier(...) <[||]> SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER

20

Page 21: Универсальный сигнатурный анализ кода на C#, Java, PHP

DSL примеры

• Password In Comment (All):Comment: <[ "(?i)password(?-i)\s*\=" ]>

• SQL Injection (All):<["(?i)select(?-i)\s\w*"]> + <[~"\w*"]>

• Cookie Without Secure Attribute (PHP)session_set_cookie_params(#,#,#) // без четвертого аргумента.

21

Page 22: Универсальный сигнатурный анализ кода на C#, Java, PHP

DSL примеры

• Empty try-catch block (All): try {...} catch { }

• Insecure Cookie (Java):Cookie <[cookie]> = new Cookie(...);...~<[cookie]>.setSecure(true);...response.addCookie(<[cookie]>);

22

Ivan Kochurkin
Рассказать про back-reference в регулярных выражениях и <[cookie]>.
Page 23: Универсальный сигнатурный анализ кода на C#, Java, PHP

Тестирование

• All In One – Тестирование парсинга разнообразного синтаксиса.• Patterns – Детекция всех известных паттернов в подготовленных файлах.• Patterns with Errors – то же, что и Patterns, но с синтаксическими

ошибками.• Прогон на проектах:

– WebGoat.NET (C#)– WebGoat (Java)– WebGoatPHP (PHP)– phpBB3 (PHP)– Zend Framework (PHP)– Yii (PHP)

23

Ivan Kochurkin
Добавить видеодемонстрацию работы AI с ядром PM.
Page 24: Универсальный сигнатурный анализ кода на C#, Java, PHP