One place for hosting & domains

      программ

      Как использовать субпроцесс для запуска внешних программ в Python 3


      Автор выбрал COVID-19 Relief Fund для получения пожертвования в рамках программы Write for DOnations.

      Введение

      Python 3 включает модуль subprocess для запуска внешних программ и чтения их выводов в коде Python.

      Модуль subprocess может оказаться вам полезен, если вы хотите использовать на своем компьютере другую программу из вашего кода Python. Например, вы можете запустить git из своего кода Python для извлечения в ваш проект, отслеживаемый в системе управления версиями git. Поскольку с помощью модуля subprocess можно контролировать любую программу, доступную на вашем компьютере, приведенные здесь примеры применимы к любым внешним программам, которые вы можете запустить из своего кода Python.

      subprocess содержит несколько классов и функций, но в этом обучающем модуле мы расскажем об одной из самых полезных функций модуля subprocess: subprocess.run. Мы изучим разные схемы его применения и основные аргументы ключевых слов.

      Предварительные требования

      Чтобы получить максимум знаний из этого обучающего модуля, рекомендуется иметь небольшое представление о программировании на Python 3. Дополнительную информацию можно найти в следующих обучающих руководствах:

      Запуск внешней программы

      Теперь вы можете использовать функцию subprocess.run для запуска внешней программы с кода Python. Прежде всего необходимо импортировать в программу модули subprocess и sys:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "print('ocean')"])
      

      Если вы запустите их, вы увидите следующий вывод:

      Output

      ocean

      Рассмотрим этот пример:

      • sys.executable — абсолютный путь к исполняемому файлу Python, из которого изначально была запущена ваша программа. Например, sys.executable может быть путем, таким как /usr/local/bin/python.
      • subprocess.run получает список строк, состоящих из компонентов команды, которую мы пытаемся запустить. Поскольку первой строкой мы передаем sys.executable, мы предписываем subprocess.run запустить новую программу Python.
      • Компонент -c — это опция командной строки python, позволяющая передать строку с целой программой Python для исполнения. В нашем случае мы передаем программу, которая распечатывает строку ocean.

      Вы можете представить каждую запись в списке, отправляемом в subprocess.run, как отделенную пробелом. Например, [sys.executable, "-c", "print('ocean')"] примерно эквивалентно /usr/local/bin/python -c "print('ocean')". Обратите внимание, что subprocess автоматически заключает в кавычки компоненты команды перед их запуском в операционной системе, так что вы можете передать имя файла с пробелами.

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

      Захват вывода внешней программы

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

      Примечание. Для примеров, показанных в этом разделе, требуется версия Python 3.7 или выше. В частности, в версию Python 3.7, выпущенную в июне 2018 г. были добавлены аргументы capture_output и ключевое слово text.

      Дополним наш предыдущий пример:

      import subprocess
      import sys
      
      result = subprocess.run(
          [sys.executable, "-c", "print('ocean')"], capture_output=True, text=True
      )
      print("stdout:", result.stdout)
      print("stderr:", result.stderr)
      

      Если запустить этот код, результат будет выглядеть следующим образом:

      Output

      stdout: ocean stderr:

      Этот пример в целом аналогичен рассмотренному в первом разделе, и мы все еще запускаем субпроцесс для печати ocean. Однако теперь мы передаем аргументы ключевых слов capture_output=True и text=True в subprocess.run.

      subprocess.run возвращает объект subprocess.CompletedProcess, который привязан к результату. Объект subprocess.CompletedProcess содержит детали о коде выхода внешней программы и ее выводе. Благодаря capture_output=True в result.stdout и result.stderr вносится соответствующий вывод внешней программы. По умолчанию result.stdout и result.stderr привязаны как байты, но аргумент ключевого слова text=True предписывает Python декодировать байты в строки.

      В разделе вывода stdout — это ocean (плюс новая строка в конце, где print добавляет явность), и у нас нет stderr.

      Рассмотрим пример, где значение stderr не пустое:

      import subprocess
      import sys
      
      result = subprocess.run(
          [sys.executable, "-c", "raise ValueError('oops')"], capture_output=True, text=True
      )
      print("stdout:", result.stdout)
      print("stderr:", result.stderr)
      

      Если запустить этот код, вывод будет выглядеть следующим образом:

      Output

      stdout: stderr: Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops

      Этот код запускает субпроцесс Python, который немедленно выдает ошибку ValueError. При просмотре окончательного результата мы не видим ничего в stdout и видим результат Traceback ошибки ValueError в stderr. Это связано с тем, что по умолчанию Python записывает Traceback необработанного исключения в stderr.

      Создание исключения при коде ошибки выхода

      Иногда бывает полезно создать исключение, если при выходе из работающей программы выдается код ошибки выхода. Программы, выдающие нулевой код ошибки при выходе считаются успешными, однако при ненулевом коде ошибки выхода считается, что в программе произошла ошибка. Например, данная система может быть полезной, если нам нужно выдавать исключение при запуске файлов git ls в каталоге, не являющемся репозиторием git.

      Мы можем использовать аргумент ключевого слова check=True, чтобы subprocess.run выдавал исключение в случае возврата внешней программой ненулевого кода ошибки выхода:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"], check=True)
      

      Если запустить этот код, вывод будет выглядеть следующим образом:

      Output

      Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 512, in run raise CalledProcessError(retcode, process.args, subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.

      Этот вывод показывает, что мы запустили субпроцесс, выдавший ошибку, которая выводится в stderr на нашем терминале. Затем subprocess.run добросовестно выдал ошибку subprocess.CalledProcessError от нашего имени в нашей основной программе Python.

      Также модуль subprocess включает метод subprocess.CompletedProcess.check_returncode, который можно использовать с похожим результатом:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"])
      result.check_returncode()
      

      Если мы запустим этот код, мы получим:

      Output

      Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 444, in check_returncode raise CalledProcessError(self.returncode, self.args, self.stdout, subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.

      Поскольку мы не передали check=True в subprocess.run, мы успешно привязали экземпляр subprocess.CompletedProcess к результату, хотя наша программа выполнила выход с ненулевым кодом ошибки. При этом при вызове result.check_returncode() выдается ошибка subprocess.CalledProcessError, поскольку модуль обнаруживает код ошибки выхода для завершенного процесса.

      Использование timeout для раннего выхода из программ

      subprocess.run имеет аргумент timeout, позволяющий остановить внешнюю программу, если ее выполнение занимает слишком много времени:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "import time; time.sleep(2)"], timeout=1)
      

      Если запустить этот код, результат будет выглядеть следующим образом:

      Output

      Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 491, in run stdout, stderr = process.communicate(input, timeout=timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1024, in communicate stdout, stderr = self._communicate(input, endtime, timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1892, in _communicate self.wait(timeout=self._remaining_time(endtime)) File "/usr/local/lib/python3.8/subprocess.py", line 1079, in wait return self._wait(timeout=timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1796, in _wait raise TimeoutExpired(self.args, timeout) subprocess.TimeoutExpired: Command '['/usr/local/bin/python', '-c', 'import time; time.sleep(2)']' timed out after 0.9997982999999522 seconds

      Субпроцесс, который мы попытались запустить, использовал функцию time.sleep для сна в течение 2 секунд. Однако мы передали аргумент с ключевым словом timeout=1 в subprocess.run, чтобы закрыть наш субпроцесс по таймауту по прошествии 1 секунды. Это объясняет, почему при вызове subprocess.run в конечном итоге было выдано исключение subprocess.TimeoutExpired.

      Обратите внимание, что аргумент ключевого слова timeout для subprocess.run является приблизительным. Python постарается остановить субпроцесс после заданного в аргументе timeout количества секунд, но это не обязательно будет точным.

      Передача ввода в программы

      Иногда программы ожидают передачи ввода через stdin.

      Аргумент ключевого слова input для subprocess.run позволяет передавать данные в stdin субпроцесса. Например:

      import subprocess
      import sys
      
      result = subprocess.run(
          [sys.executable, "-c", "import sys; print(sys.stdin.read())"], input=b"underwater"
      )
      

      Запустив этот код, мы получим примерно следующий вывод:

      Output

      underwater

      В данном случае мы передали байты underwater в input. Наш целевой субпроцесс использовал sys.stdin для чтения переданного в stdin аргумента (underwater) и вывел его в результатах.

      Аргумент ключевого слова input может быть полезен, если вы хотите создать цепочку из нескольких вызовов subprocess.run с передачей вывода одной программы как ввода в другую программу.

      Заключение

      Модуль subprocess — это мощный компонент стандартной библиотеки Python, позволяющий легко запускать внешние программы и просматривать их вывод. В этом обучающем модуле вы научились использовать subprocess.run для управления внешними программами, передачи ввода в эти программы, анализа их вывода и проверки возвращаемых ими кодов.

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



      Source link

      Установка и использование Radamsa для фаззи-тестирования программ и сетевых служб в Ubuntu 18.04


      Автор выбрал Electronic Frontier Foundation Inc для получения пожертвований в рамках программы Write for DOnations.

      Введение

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

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

      Radamsa — инструмент для фаззинга с открытым исходным кодом, позволяющий генерировать тестовые примеры на основе задаваемых пользователем входных данных. Radamsa полностью поддерживает скрипты и успешно выявляет уязвимости в реальных приложениях, таких как Gzip.

      В этом обучающем руководстве мы выполним установку Radamsa и используем этот инструмент для фаззи-тестирования сетевых приложений и приложений командной строки с использованием собственных примеров.

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

      Предварительные требования

      Для прохождения этого обучающего руководства вам потребуется следующее:

      • Один сервер Ubuntu 18.04, настроенный в соответствии с указаниями руководства Начальная настройка сервера Ubuntu 18.04, включая пользователя sudo без привилегий root и активный брандмауэр для блокирования ненужных портов.
      • Приложение командной строки или сетевое приложение, которое вы хотите протестировать, например Gzip, Tcpdump, Bind, Apache, jq или любое другое приложение по вашему выбору. Для целей этого обучающего руководства в качестве примера мы будем использовать jq.

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

      Подготовив все вышеперечисленное, войдите на сервер без привилегий root, чтобы начать подготовку.

      Шаг 1 — Установка Radamsa

      Прежде всего, мы загрузим и выполним компиляцию Radamsa для использования этого инструмента в нашей системе. Исходный код Radamsa доступен в официальном репозитории на GitLab.

      Для начала мы обновим указатель локальных пакетов, чтобы отразить последние изменения на предыдущих уровнях:

      Затем мы установим пакеты gcc, git, make и wget, необходимые для компиляции исходного кода в исполняемый двоичный файл:

      • sudo apt install gcc git make wget

      После подтверждения установки apt выполнит загрузку и установку указанных пакетов и всех необходимых зависимостей.

      Затем мы загрузим копию исходного кода Radamsa, клонировав ее из репозитория на GitLab:

      • git clone https://gitlab.com/akihe/radamsa.git

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

      Далее вы можете запустить процесс компиляции с помощью make:

      Теперь мы можем установить скомпилированный двоичный файл Radamsa в директорию $PATH:

      После этого мы можем проверить установленную версию и убедиться, что все работает правильно:

      Экран результатов должен выглядеть примерно следующим образом:

      Output

      Radamsa 0.6

      Если появится сообщение об ошибке radamsa: command not found, необходимо проверить установку всех требуемых зависимостей и убедиться в отсутствии ошибок при компиляции.

      Мы установили Radamsa и теперь можем рассмотреть примеры тестов, чтобы понять, как работает Radamsa и для чего его можно использовать.

      Шаг 2 — Подготовка примеров для фаззинга

      Мы установили Radamsa и теперь можем использовать его для рассмотрения нескольких примеров фаззинга.

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

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

      Вначале следует передать в Radamsa простой текстовый элемент и посмотреть, что произойдет в результате:

      • echo "Hello, world!" | radamsa

      Вводимые данные будут изменены (фаззинг) и получится пример для тестирования:

      Output

      Hello,, world!

      В данном случае Radamsa добавил запятую между Hello и world. Это изменение может выглядеть незначительным, но в некоторых приложениях это может привести к неправильной интерпретации данных.

      Повторим попытку, запустив ту же самую команду еще раз. Вы увидите другой результат:

      Output

      Hello, '''''''wor'd!

      На этот раз мы вставили в строку несколько одинарных кавычек ('), в том числе один символ, заменивший l в слове world. Этот пример с большей вероятностью вызовет проблемы в приложении, поскольку одинарные и двойные кавычки часто используются как разделители элементов данных в списке.

      Повторим попытку еще один раз:

      Output

      Hello, $+$PATHu0000`xcalc`world!

      В данном случае Radamsa вставляет строку оболочки, которая поможет тестировать приложение на уязвимость к вставке команд.

      Мы использовали Radamsa для фаззинга вводимой строки и подготовили ряд примеров для тестирования. Далее мы используем Radamsa для фаззинга приложения командной строки.

      Шаг 3 — Фаззинг приложения командной строки

      На этом шаге мы используем Radamsa для фаззинга приложения командной строки и получения отчета о сбоях.

      Конкретные методики фаззинга различаются для разных случаев, и для каждой программы подходят свои методы. В этом обучающем руководстве мы используем в качестве примера программу командной строки jq, предназначенную для обработки данных JSON.

      Вы можете использовать любую другую программу подобного рода, если она также принимает структурированные или неструктурированные данные, выполняет с ними какие-то операции и выводит результат. В частности, данный пример также подойдет для Gzip, Grep, bc, tr и т. д.

      Если вы еще не установили jq, вы можете выполнить установку сейчас с помощью apt:

      Сейчас будет выполнена установка jq.

      Чтобы начать фаззинг, создайте образец файла JSON, который будете использовать в качестве исходного для Radamsa:

      Добавьте в файл следующие данные JSON:

      test.json

      {
        "test": "test",
        "array": [
          "item1: foo",
          "item2: bar"
        ]
      }
      

      Вы можете проверить этот файл с помощью jq, если хотите убедиться в правильности синтаксиса JSON:

      Если синтаксис JSON правильный, jq выведет файл. В противном случае будет выведено сообщение об ошибке, которое вы сможете использовать для исправления синтаксиса в случае необходимости.

      Далее мы проведем фаззинг тестового файла JSON в Radamsa и передадим его в jq. В результате jq прочитает измененный тестовый файл, а не первоначальные правильные данные JSON:

      Если Radamsa изменит данные JSON так, что синтаксис останется верным, jq выведет данные со всеми изменениями, которые были внесены Radamsa.

      Если Radamsa изменит формат данных JSON на неверный, программа jq выдаст сообщение об ошибке. Например:

      Output

      parse error: Expected separator between values at line 5, column 16

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

      Чтобы более эффективно тестировать программы на такие уязвимости, можно автоматизировать фаззинг с помощью скрипта Bash, который будет генерировать тестовые примеры, передавать их в программу и собирать результаты.

      Создайте файл с именем jq-fuzz.sh:

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

      Скопируйте скрипт в файл jq-fuzz.sh:

      jq-fuzz.sh

      #!/bin/bash
      while true; do
        radamsa test.json > input.txt
        jq . input.txt > /dev/null 2>&1
        if [ $? -gt 127 ]; then
          cp input.txt crash-`date +s%.%N`.txt
          echo "Crash found!"
        fi
      done
      

      Этот скрипт содержит оператор while, чтобы сделать его содержимое цикличным. При каждом цикле скрипта Radamsa генерирует тестовый пример на базе файла test.json и сохраняет его в файл input.txt.

      Тестовый пример input.txt пропускается через jq так, что все стандартные сообщения и сообщения об ошибках направляются в /dev/null для предотвращения переполнения экрана терминала.

      В заключение проверяется значение jq на выходе. Если значение на выходе превышает 127, это означает критическое завершение работы (сбой), и в этом случае входные данные сохраняются для последующего использования в файле с именем crash- и суффиксом текущей даты Unix с указанием секунд и наносекунд.

      Отметьте этот скрипт как исполняемый и запустите его для автоматического тестирования jq методом фаззинга:

      • chmod +x jq-fuzz.sh
      • ./jq-fuzz.sh

      Вы можете в любой момент нажать CTRL+C для остановки скрипта. Затем вы сможете проверить наличие сбоев, используя ls для вывода списка директории с созданными файлами регистрации сбоев.

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

      Мы использовали Radamsa для фаззинга приложения командной строки. Далее мы будем использовать Radamsa для фаззинга запросов сетевых служб.

      Шаг 4 — Фаззинг запросов сетевых служб

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

      Цель фаззинга сетевых служб заключается в проверке устойчивости сетевой службы к отправке клиентами неправильно сформированных и/или вредоносных данных. Многие сетевые службы, в том числе веб-серверы и серверы DNS, обычно доступны из Интернета и часто становятся целью злоумышленников. Сетевая служба, которая недостаточно устойчива к неправильно сформированным данным, может прекратить работу или даже потерять работоспособность в открытом состоянии, в результате чего злоумышленники получат доступ к важным данным, таким как ключи шифрования или пользовательские данные.

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

      Вначале нужно настроить веб-сервер для тестирования. Для этой цели можно использовать встроенный сервер разработки, входящий в состав пакета php-cli. Также для тестирования веб-сервера нам потребуется curl.

      Если у вас не установлены php-cli и/или curl, вы можете установить их с помощью apt:

      • sudo apt install php-cli curl

      Затем создайте директорию для хранения файлов веб-сервера и перейдите в нее:

      Далее создайте файл HTML с образцом текста:

      Добавьте в файл следующее:

      index.html

      <h1>Hello, world!</h1>
      

      Теперь вы можете запустить свой веб-сервер PHP. Вам потребуется возможность просматривать журнал веб-сервера во время использования другого сеанса терминала, и поэтому нужно открыть другой сеанс терминала и канал SSH для подключения к серверу:

      • cd ~/www
      • php -S localhost:8080

      Будет выведен текст следующего вида:

      Output

      PHP 7.2.24-0ubuntu0.18.04.1 Development Server started at Wed Jan 1 16:06:41 2020 Listening on http://localhost:8080 Document root is /home/user/www Press Ctrl-C to quit.

      Теперь вы можете вернуться к исходному сеансу терминала и проверить работу веб-сервера с помощью curl:

      В результате будет выведен образец файла index.html, который вы создали ранее:

      Output

      <h1>Hello, world!</h1>

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

      Мы настроили тестовый веб-сервер и теперь можем начать фаззинг с помощью Radamsa.

      Для начала нам потребуется создать образец запроса HTTP, который будет использоваться в качестве исходных данных Radamsa. Создайте новый файл для его сохранения:

      Затем скопируйте в файл следующий образец запроса HTTP:

      http-request.txt

      GET / HTTP/1.1
      Host: localhost:8080
      User-Agent: test
      Accept: */*
      

      Теперь мы можем использовать Radamsa для отправки этого запроса HTTP на локальный веб-сервер. Для этого нужно использовать Radamsa в качестве клиента TCP, что можно сделать посредством указания IP-адреса и порта для подключения:

      • radamsa -o 127.0.0.1:8080 http-request.txt

      Примечание. Использование Radamsa в качестве клиента TCP может привести к передаче через сеть неправильно сформированных или вредоносных данных. Это может повлечь за собой неисправности, и поэтому вы должны использовать только сети, которые вам разрешено тестировать, а лучше всего использовать только адрес localhost (127.0.0.1).

      Если вы просмотрите журналы локального веб-сервера, вы увидите, что он получал запросы, однако вряд ли обрабатывал их, поскольку они были недопустимыми или неправильно сформированными.

      Журналы будут отображаться во втором окне терминала:

      Output

      [Wed Jan 1 16:26:49 2020] 127.0.0.1:49334 Invalid request (Unexpected EOF) [Wed Jan 1 16:28:04 2020] 127.0.0.1:49336 Invalid request (Malformed HTTP request) [Wed Jan 1 16:28:05 2020] 127.0.0.1:49338 Invalid request (Malformed HTTP request) [Wed Jan 1 16:28:07 2020] 127.0.0.1:49340 Invalid request (Unexpected EOF) [Wed Jan 1 16:28:08 2020] 127.0.0.1:49342 Invalid request (Malformed HTTP request)

      Для получения оптимальных результатов и гарантированной регистрации сбоев можно использовать скрипт автоматизации, аналогичный использованному на шаге 3. Также следует попробовать использовать более сложный исходный файл, который может содержать такие дополнения, как дополнительные заголовки HTTP.

      Мы провели тестирование сетевой службы методом фаззинга, использовав Radamsa в качестве клиента TCP. Теперь мы проведем фаззинг сетевого клиента, используя Radamsa в качестве сервера.

      Шаг 5 — Фаззинг сетевых клиентских приложений

      На этом шаге мы используем Radamsa для тестирования сетевого клиентского приложения методом фаззинга. Это реализуется посредством перехвата ответов сетевой службы и их искажения до поступления на клиент.

      Цель такого фаззинга заключается в проверке устойчивости клиентских приложений сети к получению неправильно сформированных или вредоносных данных от сетевых служб. В качестве примера можно привести тестирование браузера (клиента), получающего неправильно сформированный код HTML от веб-сервера (сетевой службы) или тестирование клиента DNS, получающего неправильно сформированные ответы DNS от сервера DNS.

      Как и в случае с фаззингом приложений командной строки или сетевых служб, конкретная методика фаззинга разных сетевых клиентских приложений может значительно различаться, однако в этом примере мы используем whois, простое приложение для приема/передачи на базе TCP.

      Приложение whois используется для отправки запросов на серверы WHOIS и получения записей WHOIS в составе ответов. WHOIS работает через порт TCP 43 в формате простого текста, что делает его отличным вариантом для фаззинга сетевого клиента.

      Если у вас еще нет приложения whois, вы можете установить его с помощью apt:

      Прежде всего, необходимо получить пример ответа whois для использования в качестве исходных данных. Для этого можно создать запрос whois и сохранить результат в файл. Здесь вы можете использовать любой домен, поскольку будете тестировать всю программу whois локально, используя образцы данных:

      • whois example.com > whois.txt

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

      • radamsa -o :4343 whois.txt -n inf

      Теперь Radamsa будет работать в режиме сервера TCP и будет отправлять искаженную версию файла whois.txt при каждой попытке подключения к серверу, вне зависимости от того, какие данные запроса будут получены.

      Теперь вы можете перейти к тестированию клиентского приложения whois. Вам потребуется нормальный запрос whois для любого домена по вашему выбору (не обязательно для того, для которого предназначен образец), но при этом whois должен указывать на ваш локальный сервер Radamsa:

      • whois -h localhost:4343 example.com

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

      Как и в случае с фаззингом сетевых служб, вы можете написать скрипт автоматизации, аналогичный использованному на шаге 3, чтобы повысить эффективность фаззинга сетевых клиентов и гарантировать регистрацию сбоев.

      На этом заключительном шаге мы использовали Radamsa для тестирования сетевого клиентского приложения методом фаззинга.

      Заключение

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

      Если вы хотите узнать больше о Radamsa, вы можете более подробно ознакомиться с файлом Radamsa README, где содержится дополнительная техническая информация и дополнительные примеры использования:

      Также вы можете рассмотреть и другие инструменты для фаззинга, например продвинутую программу фаззинга American Fuzzy Lop (AFL), предназначенную для тестирования двоичных приложений с очень высокой скоростью и точностью:



      Source link

      Сборка и установка программ Go


      Введение

      До настоящего момента в нашей серии статей о программировании на языке Go вы использовали команду go run для автоматической компиляции исходного кода и запуска получаемого исполняемого файла. Хотя эта команда полезна при тестировании кода в командной строке, распределении или развертывании, ваше приложение требует, чтобы вы выполняли сборку кода в распределяемый бинарный исполняемый файл или отдельный файл, содержащий машинный байт-код, который может запускать ваше приложение. Для этого вы можете использовать цепь инструментов Go для сборки и установки вашей программы.

      В Go процесс перевода исходного кода в бинарный исполняемый файл называется сборкой. После получения исполняемого файла он будет содержать не только ваше приложение, но и вспомогательный код, необходимый для выполнения бинарного файла в целевой платформе. Это означает, что бинарному файлу Go не требуются зависимости, например инструменты Go, для запуска в новой системе, в отличие от других языков, таких как Ruby, Python или Node.js. Размещение этих исполняемых файлов в путь исполняемого файла в вашей системе позволит запускать программу из любого места в вашей системе. Этот процесс называется установкой программы в вашей системе.

      В этом обучающем руководстве вы будете использовать цепь инструментов Go для запуска, сборки и установки примера программы Hello, World!, позволяющей использовать, распространять и эффективно развертывать будущие приложения.

      Предварительные требования

      Для выполнения примера из этой статьи вам потребуется следующее:

      Настройка и запуск бинарного файла Go

      Сначала создайте приложение для использования в качестве примера для демонстрации цепи инструментов Go. Для этого вы будете использовать классический пример программы “Hello, World!” из руководства Как написать свою первую программу на Go.

      Создайте директорию greeter в директории src:

      Теперь перейдите в новую директорию и создайте файл main.go в текстовом редакторе на ваш выбор:

      После открытия файла добавьте следующий код:

      src/greeter/main.go

      package main
      
      import "fmt"
      
      func main() {
          fmt.Println("Hello, World!")
      }
      

      При запуске эта программа будет выводить фразу Hello, World! в консоль, а затем успешно завершать работу.

      Сохраните и закройте файл.

      Чтобы протестировать программу, воспользуйтесь командой go run, как вы уже делали в предыдущих руководствах:

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

      Output

      Hello, World!

      Как было упомянуто выше, команда go run выполняет сборку вашего исходного файла в исполняемый бинарный файл, а затем запускает скомпилированную программу. Однако в этом руководстве мы выполним сборку таким образом, чтобы вы смогли предоставить общий доступ к вашему файлу и распространять его при желании. Чтобы сделать это, мы будем использовать команду go build в следующем шаге.

      Сборка бинарных файлов Go с помощью команды go build

      Используя go build, вы можете сгенерировать исполняемый бинарный файл для нашего примера приложения Go, позволяющего распределять и развертывать программу, где захотите.

      Попробуйте сделать это с main.go. В директории greeter запустите следующую команду:

      Если вы не предоставите аргумент этой команде, go build будет автоматически компилировать программу main.go в текущем каталоге. Команда будет использовать все файлы *.go в директории. Также она выполнит сборку всего вспомогательного кода, необходимого для исполнения бинарного файла на любом компьютере с той же системной архитектурой, независимо от того, есть ли в системе исходные файлы .go или даже установка Go.

      В данном случае вы создали приложение greeter в исполняемом файле, добавленном в текущую директорию. Проверьте это, запустив команду ls:

      Если вы используете macOS или Linux, то найдете новый исполняемый файл, который назван по директории, где вы выполнили сборку вашей программы:

      Output

      greeter main.go

      Примечание. В Windows ваш исполняемый файл будет называться greeter.exe.

      По умолчанию go build будет генерировать исполняемый файл для текущей платформы и архитектуры. Например, при сборке в системе linux/386 исполняемый файл будет совместимым с любой другой системой linux/386, даже если там не установлен Go. Go поддерживает сборку для других платформ и архитектур, о чем вы можете узнать подробнее из нашей статьи Сборка приложений Go для разных операционных систем и архитектур.

      Теперь, когда вы создали исполняемый файл, запустите его, чтобы убедиться, что бинарный файл сформирован корректно. В macOS или Linux запустите следующую команду:

      В Windows запустите следующую команду:

      Вывод бинарного файла будет соответствовать выводу, когда вы запустили программу с командой go run:

      Output

      Hello, World!

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

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

      Изменение имени бинарного файла

      Теперь, когда вы знаете, как сгенерировать исполняемый файл, следующим шагом будет определение того, как Go выбирает имя бинарного файла и как можно задать собственное имя для вашего проекта.

      При запуске go build по умолчанию Go будет выполнять автоматический выбор имени сгенерированного исполняемого файла. Go делает это двумя способами: если вы используете модули Go, то Go будет использовать последнюю часть имени модуля, в противном случае Go использует имя текущей директории. Этот метод использовался в последнем разделе, когда вы создали директорию greeter, перешли в нее и запустили команду go build.

      Давайте более внимательно изучим метод с использованием модуля. Если у вас есть файл go.mod в проекте с объявлением module​​, например, как показано здесь:

      go.mod

      module github.com/sammy/shark
      

      Тогда имя сгенерированного исполняемого файла по умолчанию будет shark>.

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

      Чтобы протестировать это, измените имя исполняемого файла, полученного в последнем разделе, на hello и разместите его в подпапке bin. Вам не нужно создавать эту папку, Go сделает это самостоятельно при сборке.

      Запустите следующую команду go build с флагом -o:

      Флаг -o заставляет Go сопоставлять вывод команды с предпочитаемым вами аргументом. В данном случае результатом является новый исполняемый файл с именем hello в подпапке с именем bin.

      Чтобы протестировать новый исполняемый файл, перейдите в новую директорию и запустите бинарный файл:

      Результат будет выглядеть следующим образом:

      Output

      Hello, World!

      Теперь вы можете настроить имя исполняемого файла согласно требованиям вашего проекта, завершив изучение сборки бинарных файлов в Go. Но при использовании команды go build вы все равно ограничены требованием запуска вашего бинарного файла из текущей директории. Чтобы использовать вновь собранные исполняемые файлы из любого места в системе, вы можете установить их с помощью команды go install.

      Установка программ Go с помощью go install

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

      Чтобы понять, что мы под этим подразумеваем, вы будете использовать команду go install для установки примера приложения.

      Команда go install ведет себя почти идентично go build, но вместо того чтобы оставлять исполняемый файл в текущей директории или директории, отмеченной флагом -o, она помещает исполняемый файл в директорию $GOPATH/bin.

      Чтобы найти местоположение вашей директории $GOPATH, запустите следующую команду:

      Вывод, который вы получите, будет отличаться, но по умолчанию это будет директория go внутри вашей директории $HOME:

      Output

      $HOME/go

      Поскольку go install будет помещать сгенерированные исполняемые файлы в субдиректорию $GOPATH с именем bin, эта директория должна быть добавлена в переменную среды $PATH. Это описано в разделе Создание вашего рабочего пространства Go в предварительной статье Установка Go и настройка локальной среды программирования.

      После настройки директории $GOPATH/bin вернитесь в директорию greeter:

      Теперь запустите команду:

      В результате вы создадите бинарный файл и поместите его в $GOPATH/bin. Чтобы протестировать, запустите следующую команду:

      В результате вы увидите список содержимого $GOPATH/bin:

      Output

      greeter

      Примечание. Команда go install не поддерживает флаг -o, поэтому она использует одно из имен по умолчанию, описанных ранее, для имени исполняемого файла.

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

      Используйте следующую команду для запуска программы:

      В результате вы получите следующее:

      Output

      Hello, World!

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

      Заключение

      В этом обучающем руководстве вы продемонстрировали, как цепь инструментов Go облегчает сборку исполняемых бинарных файлов из исходного кода. Эти бинарные файлы можно распределять для запуска в других системах, даже там, где нет инструментов и среды Go. Также вы использовали команду go install для автоматической сборки и установки наших программ в качестве исполняемых файлов в $PATH системы. С помощью go build и go install у вас есть возможность предоставлять общий доступ к вашему приложению и использовать его по вашему желанию.

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



      Source link