Процесс сигнатурного анализа кода в нашем проекте PT Application Inspector разбит на следующие этапы:


  1. парсинг в зависимое от языка представление (abstract syntax tree, AST);
  2. преобразование AST в независимый от языка унифицированный формат;
  3. непосредственное сопоставление с шаблонами, описанными на DSL.

О первых двух этапах было рассказано в предыдущих статьях "Теория и практика парсинга исходников с помощью ANTLR и Roslyn" и "Обработка древовидных структур и унифицированное AST". Данная статья посвящена третьему этапу, а именно: различным способам описания шаблонов, разработке специализированного языка (DSL) для их описания, а также примерам шаблонов на этом языке.



Содержание




Способы описания шаблонов


  • Заданные в коде шаблоны (hardcoded);
  • JSON, XML или другой язык разметки;
  • DSL, предметно-ориентированный язык .


Hardcoded


Шаблоны можно записывать в ручную прямо в коде. Для этого не требуется разрабатывать парсер. Этот способ не подходит для не разработчиков, зато может использоваться для написания юнит-тестов. Также для записи новых шаблонов требуется перекомпиляция всей программы.



JSON, XML или другой язык разметки


Части сопоставляемого AST можно непосредственно сохранять и загружать из JSON или других форматов. При таком подходе шаблоны можно будет загружать извне, однако синтаксис будет громоздким и не подойдет для редактирования пользователем. Однако этот способ можно использовать для сериализации древовидных структур. (О способах сериализации древовидных структур в .NET и их обходе будет рассказано в следующей статье.)



Собственный язык описания шаблонов, DSL


Третий подход заключается в разработке специального предметно-ориентированного языка, который можно было бы легко редактировать, который был бы лаконичным, но при этом обладал достаточной выразительной мощностью для описания существующих и будущих шаблонов. Недостатком такого подхода является необходимость разработки синтаксиса и парсера для него.



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


Как уже говорилось в первой статье, не все шаблоны можно просто и удобно описать с помощью регулярных выражений. DSL является смесью регулярных выражений и частоиспользуемых конструкций из популярных языков программирования. Кроме того, данный язык предназначается для конкретной предметной области и не предполагается, что он будет использоваться в качестве какого-либо стандарта.



Синтаксис


Во второй статье цикла мы говорили, что основными конструкциями в императивных языках программирования являются примитивные типы (literals), выражения (expressions) и инструкции (statements). При разработке DSL мы сделали все так же. Примеры выражений:


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

Инструкции создаются путем добавления точки с запятой в конец выражения.


Литералами же являются примитивные типы, такие как:


  • Id; идентификатор;
  • String; строка, обособляется двойными кавычками;
  • Int; целое число;
  • Bool; булево значение.

Эти литералы позволяют описывать простые конструкции, однако с помощью них нельзя, например, описывать диапазоны чисел, регулярные выражения. Для поддержки таких более сложных случаев были введены расширенные конструкции (PatternStatement, PatternExpression, PatternLiteral). Такие конструкции обособляются специальными скобками <[ и ]>. Подобный синтаксис был заимствован из Nemerle (в нем такие скобки используются для квазицитирования, т. е. для преобразования кода внутри них в AST Nemerle).


Примеры поддерживаемых расширенных конструкций представлены в списке ниже. Для некоторых также конструкций предусмотрен синтаксический сахар, позволяющий сократить запись:


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


Примеры шаблонов



Жестко заданный пароль (все языки)


(#.)?<[(?i)password(?-i)]> = <["\w*"]>


  • #; любое выражение, может отсутствовать;
  • <[(?i)password(?-i)]>; регулярное выражение типа Id, может быть записано в любом регистре;
  • <["\w*"]>; регулярное выражение типа String;



Слабый генератор случайных чисел (C#, Java)


new Random(...)


Уязвимость заключается в использовании небезопасного алгоритма генерации случайных чисел. Пока что такие случаи отслеживаются с помощью поиска конструктора стандартного класса Random.



Утечка отладочной информации (PHP)


Configure.<[(?i)^write$]>("debug", <[1..9]>)


  • <[(?i)^write$]>; регулярное выражение типа ID, не зависит от регистра и определяет только точные вхождения;
  • ("debug", <[1..9]>); аргументы функции;
  • <[1..9]>; диапазон целых чисел от 1 до 9.


Небезопасное SSL-соединение (Java)


new AllowAllHostnameVerifier(...) <[||]> SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER.


Использование "логического ИЛИ" для целых синтаксических конструкций.



Пароль в комментарии (все языки)


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


Поиск комментариев в исходном коде. Причем в C#, Java, PHP, как известно, однострочные комментарии начинаются с двойного слэша //, а в SQL-подобных языках — с двойного дефиса --.



SQL-инъекция (C#, Java, PHP)


<["(?i)select\s\w*"]> + <[~"\w*"]>


Простая SQL инъекция: конкатенация любой строки, начинающейся с select и не строковым выражением в правой части.



Куки без атрибута безопасности (PHP)


session_set_cookie_params(#,#,#)


Установка куки без флага защищенности, который задается в четвертом аргументе.



Пустой блок обработки исключения (все языки)


try {...} catch { }


Пустой блок обработки исключений. В C# модуль будет находить такой код:


try
{
}
catch
{
}

В T-SQL такой:


BEGIN TRY
    SELECT 1/0 AS DivideByZero
END TRY
BEGIN CATCH
END CATCH

А в PL/SQL такой:


PROCEDURE empty_default_exception_handler IS
BEGIN
    INSERT INTO table1 VALUES(1, 2, 3, 4);
    COMMIT;
  EXCEPTION
    WHEN OTHERS THEN NULL;
END;


Небезопасный куки (Java)


Cookie <[@cookie]> = new Cookie(...);
...
~<[@cookie]>.setSecure(true);
...
response.addCookie(<[@cookie]>);


Добавление куки без установленного флага защищенности. Несмотря на то что данный шаблон правильнее реализовывать в taint-анализе, его удалось реализовать и с помощью более примитивного алгоритма сопоставления. В нем используется прикрепленная переменная @cookie, отрицание выражения и произвольное количество утверждений.



Перехват незакрытого курсора (PL/SQL, T-SQL)


PL/SQL

<[@cursor]> = DBMS_SQL.OPEN_CURSOR;
...
<[~]>DBMS_SQL.CLOSE_CURSOR(<[@cursor]>);

T-SQL

declare_cursor(<[@cursor]>);
...
<[~]>deallocate(<[@cursor]>);

Незакрытый курсор потенциально может эксплуатироваться менее привилегированным пользователем.


В T-SQL будет находиться такой код:


DECLARE Employee_Cursor CURSOR FOR
SELECT EmployeeID, Title FROM AdventureWorks2012.HumanResources.Employee;
OPEN Employee_Cursor;
FETCH NEXT FROM Employee_Cursor;
WHILE @@FETCH_STATUS = 0
   BEGIN
      FETCH NEXT FROM Employee_Cursor;
   END;
--DEALLOCATE Employee_Cursor; is missing
GO


Чрезмерно расширенные полномочия (PL/SQL, T-SQL)


grant_all(...)


Данный недостаток чреват тем, что пользователю может быть выдано больше привилегий, чем это требуется.


Будет находиться такой код:
GRANT ALL ON employees TO john_doe;



Заключение


Для демонстрации работы нашего модуля мы подготовили видео, в котором показан процесс поиска определенных шаблонов в коде на различных языках программирования (C#, Java, PHP) в нашем продукте PT Application Inspector. Демонстрируется также корректная обработка синтаксических ошибок, которая была затронута в первой статье нашей серии.




В следующих статьях мы расскажем:


  • о сравнении, сериализации и обходе древовидных структур в .NET;
  • построении CFG, DFG и taint-анализе.
Поделиться с друзьями
-->

Комментарии (6)


  1. gearbox
    17.05.2016 17:57

    спасибо за статью! На TypeScript не поглядываете? Очень хотелось бы увидеть его в списке.


    1. KvanTTT
      18.05.2016 00:56

      У нас в планах есть поддержка JavaScript.


  1. Irishdash
    18.05.2016 00:46

    Разве использование регулярных выражений не повлияет существено на производительность?


    1. KvanTTT
      18.05.2016 01:34

      Нет, а почему они должны существенно на нее повлиять? Регулярные выражения сопоставляются не со всеми токенами, а с теми, до которых дошли в результате сопоставления шаблона в виде дерева с основным AST. К тому они же компилируются при создании (на платформе .NET используется параметр RegexOptions.Compiled).


      В целом же модуль Pattern Matching работает очень быстро, но и возможности у него достаточно сильно ограничены.


  1. PavelMSTU
    19.05.2016 20:11
    +1

    Спасибо за пост!

    А эту задачу пробовали решать?: Surreptitiously Weakening Cryptographic Systems
    Мне кажется, или подход может быть тот же?


    1. KvanTTT
      20.05.2016 14:31

      А можно вкратце для всех объяснить как DSL можно там использовать?