Pull to refresh

Использование фреймворков модульного тестирования для выявления уязвимостей

Reading time6 min
Views1.8K
Original author: Vikrant Singh Chauhan.
image

Формулировка проблемы


Поиск уязвимостей — сложный процесс, а еще сложнее пользоваться уязвимостями на нескольких целях в разных конфигурациях. Именно по этой причине существует множество фреймворков, таких как Metasploit, и различных сканеров уязвимостей, таких как nuclei. Так много фреймворков и инструментов, но эксплойты по-прежнему осуществляются при помощи простых скриптов, написанных на различных языках, таких как Python и Go. Почему так? Всё дело в гибкости.

Написание собственных скриптов — лучший выбор, и в наши дни исследователи безопасности с удовольствием пишут код на Python и Go. Однако есть несколько проблем с простыми скриптами. Например, когда у вас есть сотни скриптов, и вам нужно найти один конкретный скрипт для эксплуатации одной части, основанной на обнаружении. Для этого я видел, как исследователи безопасности писали свои собственные инструменты с простыми функциями, такими как обнаружение и методы эксплуатации, а затем запускали код в нескольких потоках. Думаю, вы уже пытались это сделать. Может быть, у вас уже есть такая конфигурация или вы используете Nuclei для достижения той же цели. Это просто, и работа действительно выполняется, но есть ли способ получше? Да, есть. Модульное тестирование.

Что такое модульное тестирование и зачем его использовать для сканирования уязвимостей?


Модульное тестирование — это методология тестирования небольших блоков или отдельных частей кода. Вы можете протестировать небольшую функцию и проверить, соответствует ли результат ожиданиям или нет. Именно это делает проверка на уязвимости. Запустите небольшой кусок кода, проверьте, правильный результат или нет.

Причина, по которой вы должны использовать модульные тесты вместо сканера уязвимостей, проста. Вы получаете гибкость написания кода на выбранном вами языке программирования, а также гибкость операций на уровне системы. Вы также можете просто импортировать и протестировать небольшой фрагмент кода. Вы можете проводить веб-тестирование с помощью безголовых браузеров и клиентских библиотек HTTP, вы можете проводить бинарную эксплуатацию с помощью функций системного уровня, вы также можете проводить статический и динамический анализ кода. Если вы пишете собственные скрипты, вам придется написать многопоточное приложение, которое может загружать скрипты в качестве аддона/плагина/расширения, что само по себе является трудоемким проектом. Важнейшее достоинство модульных тестов для обнаружения эксплойтов заключается в том, что вы можете легко делиться кодом с разработчиками для регрессионного тестирования (идея тестирования кода снова и снова на каждой итерации, чтобы старый код не отказывал). Такую гибкость сложно реализовать в сканерах уязвимостей.

Так как же работает модульное тестирование?


При модульном тестировании у нас, по сути, есть существующий код, который нужно протестировать. Мы добавляем фреймворк для тестирования и пишем класс или функцию под названием «Test», которая имеет методы для постулирования некоторых предположений. Затем вы можете использовать команды тестирования, такие как npm test, python -m nose2, go test, dotnet test или любые другие, которые вы можете использовать в выбранном вами фреймворке для тестирования. Если вам нужно научиться или отточить навыки модульного тестирования, найдите подходящие ресурсы по выбранному вами языку и фреймворку. Вот несколько хороших руководств:

В дальнейшем я сосредоточусь на фреймворке xUnit, поскольку уже некоторое время пользуюсь им в качестве сканера уязвимостей.

xUnit как средство поиска уязвимостей
Для создания сканера уязвимостей на основе модульного тестирования нужны только две вещи. Способ многопоточного выполнения кода и способ регистрации событий. Помимо этого, достаточно простого проекта, в котором будет храниться весь код. Создайте новый проект xunit с,

dotnet new xunit -o VulnScanner

Он должен создать новый каталог с файлом proj и базовым модульным тестом UnitTest1.cs.
Теперь давайте сделаем его многопоточным. В нашем проекте создайте новый файл xunit.runner.json со следующим содержимым:

{
    "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
    "parallelizeAssembly": true,
    "maxParallelThreads": 40
}

В параметре maxParallelThreads можно определить, сколько потоков вы хотите задействовать для выполнения тестов. Схема $schema необязательна и помогает только в завершении кода и подсветке в Visual Studio (Code). Теперь нам нужно указать компилятору скопировать нашу конфигурацию в каталог сборки. Откройте ваш файл proj и добавьте в тег image следующее:

    <Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />

Ваш файл proj должен выглядеть следующим образом:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <IsPackable>false</IsPackable>
    <IsTestProject>true</IsTestProject>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
    <PackageReference Include="xunit" Version="2.4.2" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="coverlet.collector" Version="6.0.0">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

</Project>


Вот и всё. Теперь вы можете начать писать свой код в виде тестов в UnitTest1.cs или любом другом файле тестов, и он будет выполняться многопоточно.

dotnet test

Теперь осталось сделать еще одну вещь — войти в систему. Неважно, где вы будете вести журнал. Вам просто нужно захватить вывод журнала xUnit и затем использовать его как угодно. Захват довольно прост, и вы все еще можете отправить тест. Мы перехватываем вывод, внедряя ITestOutputHelper в конструктор нашего теста.

using Xunit;
using Xunit.Abstractions;

public class UnitTest1
{
    private readonly ITestOutputHelper output;

    public UnitTest1(ITestOutputHelper output)
    {
        this.output = output;
    }

    [Fact]
    public void Test1()
    {
        string target = Environment.GetEnvironmentVariable("TARGET");
        getallUrls(target);
        Assert.Contains("/haproxy-status", target);
        output.WriteLine("Some logging here");
    }
}

Запись в output отправит ваш журнал в отладочные журналы xUnit, которые можно прочитать с помощью следующей команды.

dotnet test --logger "console;verbosity=detailed";

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

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

using Xunit.Abstractions;

namespace ScanV;

public class UnitTest2: Test
{
    public UnitTest2(ITestOutputHelper output) : base(output) { }

    /// <summary>
    /// Second test
    /// </summary>
    [Fact]
    public void Test2()
    {
        string? target = Environment.GetEnvironmentVariable("TARGET");
        if(target != null) {
            output.WriteLine($"Attacking {target}");
        }
    }
}

В оболочках типа bash можно передать входные данные в вышеуказанный тест с помощью

TARGET="example.com" dotnet test --logger "console;verbosity=detailed";

В powershell,

 & { $env:TARGET="example.com"; dotnet test --logger "console;verbosity=detailed"}

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

p/s идет Распродажа «Старый Новый год» в издательстве «Питер»
Tags:
Hubs:
Total votes 6: ↑6 and ↓0+6
Comments0

Articles

Information

Website
piter.com
Registered
Founded
Employees
201–500 employees
Location
Россия