О, вам нравится SSH? А перечислите-ка все флаги!
Приветствую
Все мы видели эти красивые схемы, демонстрирующие, как в SSH устроен проброс портов. Но, если мы с вами мыслим хотя бы немного схоже, то эти схемы оставляют у вас больше вопросов, чем дают ответов. Если вы за «красных» в области компьютерной безопасности, то, чтобы обрести в сети суперсилу и в дальнейшем бесчинствовать, вы должны понимать сеть лучше, чем те, кто её проектировал. Один из инструментов, наделяющих вас такой суперсилой — SSH. Но иногда нам мешают добиться поставленных целей сам синтаксис инструмента и другие концепции, на основе которых этот инструмент работает. Чтобы вы могли бесчинствовать эффективнее, не срывая сроков, я собрал для вас длинный список присущих SSH штук, которые я нахожу полезными. Хорошо, если вы его тоже почитаете, но составлял я его в основном для себя. Заметил, что сам я качественно усваиваю те или иные концепции, только если, изучая информацию, повторяю упражнения на клавиатуре. В этом посте я, в сущности, рассказываю, чему научился таким образом. Должен отметить, что во всех этих примерах я демонстрирую проброс портов при помощи веб-сервера, но таких же результатов можно добиться и при помощи почти любого сервиса, в частности, RDP, SQL, т.д.

Проброс локального порта (-L)
Как понятно из названия, при пробросе локального порта можно создать локальный порт, информация с которого переадресовывается на удалённый порт. Предположим, что на сервере internal-web.int располагается веб-страница, доступная только через петлевой интерфейс. Таким образом, чтобы обратиться к этой веб-странице, нужно обратиться к internal-web.int. Обойти это препятствие можно, например, воспользовавшись пробросом локального порта по SSH. Допустим, мы обращаемся по SSH к странице internal-web.int, расположенной на хост-машине campfire.int. В таком случае мы пробрасываем локальный порт, что в дальнейшем позволит нам обратиться к удалённому веб-серверу через локальный порт.
Вот как выглядит команда для этого: ssh -N -f -L 1337:127.0.0.1:80 [email protected]. Эта команда выполняется на campfire.int. Это сложная команда, поэтому давайте, как обычно, разберём каждый её флаг отдельно, чтобы сориентироваться, что именно здесь происходит.
-
‑N: так мы сообщаем SSH, что за пределы сервера мы никакие команды отправлять не будем. Без этого мы бы получили оболочку на [email protected] -
‑f: так мы переводим работу SSH в фоновый режим. Если бы мы так не сделали, то окно терминала зависло бы, и пользоваться терминалом мы бы не смогли. -
‑Lтак мы приказываем SSH пробросить локальный порт. -
1337:127.0.0.1:80: так мы приказываем SSH связать локальный порт 1337 с удалённым портом 80 (тем самым, на котором включён наш веб‑сервер).
Примечание: насколько мне известно, лучше всего это делать именно с помощью флага -L, означающего локальность слева от адреса или -R, означающего локальность порта справа от адреса.
-
[email protected] так мы сообщаем SSH, что хотим зайти под именем и паролем на удалённый сервер с правами администратора, что позволит нам открыть SSH-туннель. Напомню, что порт 1337 на нашей локальной машине будет связан с портом 80 на удалённом сервере.
-
[email protected] так мы сообщаем SSH, что хотим зайти под именем и паролем на удалённый сервер с правами администратора, что позволит нам открыть SSH‑туннель. Напомню, что порт 1337 на нашей локальной машине будет связан с портом 80 на удалённом сервере.
Теперь, обустроив проброс локального порта, можно взаимодействовать с портом 80 на internal-web.int, отправляя запросы на порт 1337 нашей локальной машины ( campfire.int).
Проброс удалённого порта (-R)
Проброс удалённого порта — это операция, противоположная пробросу локального. Допустим, у нас есть доступ к internal-web.int, и на нём располагается веб-страница, доступная только через петлевой интерфейс. Также предположим, что campfire.int не может напрямую обращаться к internal-web.int. В таком сценарии хотелось бы обратиться к internal-web.int от campfire.int. Проблема в том, что из-за брандмауэра невозможно наладить прямой обмен данными между campfire.int и internal-web.int. Пытаясь с этим справиться, обнаруживаем, что vuln-server.int доступен как с campfire.int, так и с internal-web.int. В данном случае нужно воспользоваться пробросом удалённого порта через SSH, чтобы переадресовывать данные порта 80 с internal-web.int на произвольный порт vuln-server.int. Организовав проброс удалённого порта, мы сможем открывать внутреннюю веб-страницу с internal-web.int, работающую на порте 80. Для этого направим команду curl на vuln-server.int.
Вот команда для этой цели: ssh -N -f -R 3000:127.0.0.1:80 [email protected].
-
‑
N: так мы сообщаем SSH, что за пределы сервера мы никакие команды отправлять не будем. Без этого мы бы получили оболочку на [email protected] -
‑
f: так мы переводим работу SSH в фоновый режим. Если бы мы так не сделали, то окно терминала зависло бы, и пользоваться терминалом мы бы не смогли. -
‑
Rтак мы приказываем SSH пробросить удалённый порт -
3000:127.0.0.1:80: В результате SSH должна связать удалённый порт 3000 с портом 80 локального компьютера.
Замечание
Что касается запоминания, я обнаружил, что удобнее всего понимать -L как «локальный слева от адреса». При пробросе удалённого порта с флагом -R локальный порт указывается справа от адреса. local port is on the right-hand side of the address.
-
[email protected]: так мы сообщаем SSH, что хотим зайти под именем и паролем на удалённый сервер с правами администратора, что позволит нам открыть SSH-туннель. Напомню, что порт 3000 (на vuln-server.int) будет связан с портом 80 на данном сервере.
Теперь, обустроив проброс удалённого порта, можно взаимодействовать с портом 80 на internal-web.int, отправляя запрос curl на vuln-server.int:3000

Динамический проброс порта (-D)
Динамический проброс порта при помощи опции -D — интересный вариант опосредованной передачи трафика через прокси SOCKS. Допустим, что на internal-web.int размещено веб-приложение, доступное только из внутрикорпоративной сети. Также предположим, что по SSH можно получить доступ к vuln-server.int, он находится в той же самой внутренней сети, и так нам открывается путь к internal-web.int. Вот чего хотелось бы добиться в данном случае: достучаться до веб-сервера, работающего по адресу internal-web.int, пустив через прокси весь наш трафик от campfire.int через vuln-server.int. При этом задействуются обе цепочки прокси и браузер с нашей локальной машины. Сначала давайте убедимся в том, что конфигурация в файле /etc/proxychains.conf записана правильно.
-
Socks5: сообщает цепочкам прокси, что нужно использовать socks5 (а не socks4)
-
127.0.0.1: приказывает цепочкам прокси использовать наш localhost
-
8080: именно этот порт мы будем динамически пробрасывать. Указанный здесь порт должен совпадать с тем, который вы задали с опцией ‑D в рамках вашей команды SSH.

Вот команда для этой цели: ssh -N -f -D 8080 [email protected]
-
‑N: так мы сообщаем SSH, что за пределы сервера мы никакие команды отправлять не будем. Без этого мы бы получили оболочку на [email protected]
-
‑f: так мы переводим работу SSH в фоновый режим. Если бы мы так не сделали, то окно терминала зависло бы, и пользоваться терминалом мы бы не смогли.
-
‑D 8080 приказывает SSH создать на локальной машине динамический порт, через который мы будем посылать наш трафик.
-
[email protected]: так мы сообщаем SSH, что хотим зайти под именем и паролем на удалённый сервер с правами администратора, что позволит нам открыть SSH‑туннель. Через него мы сможем проксировать наш трафик.
Организовав динамический проброс порта через 8080 и установив socks5 127.0.0.1 8080 в /etc/proxychains.conf, можно выполнить proxychains curl 192.168.1.185 и увидеть нашу веб-страницу, размещённую по адресу 192.168.1.185. Кроме того, DNS по SOCKS кажется мне ненадёжным решением, почему я и указал конкретный IP-адрес в приведённой ниже команде curl.

Теперь сконфигурируем Firefox так, чтобы можно было просматривать наш внутренний сервер через прокси SOCKS. Для этого понадобится поставить правильные настройки прокси внутри самого firefox. Для этого откроем Firefox и перейдём в: Settings -> Privacy & Security -> Network Settings. Там выберите конфигурирование прокси «вручную» и отметьте “Proxy DNS when using SOCKS V5”. Наконец, сообщите Firefox о прокси SOCKS, который мы только что настроили. Для этого задайте хосту SOCKS адрес 127.0.0.1 и порт 8080 (или другой порт, тот, который вы указали в вашей команде SSH).

Теперь, когда прокси у нас настроен, можно обратиться к веб-странице на internal-web.int, поскольку весь наш трафик перенаправляется с локальной машины через vuln-server.int. Круто, правда?

Инсталляционные серверы или jump-серверы (-J)
По сравнению с предыдущими командами перепрыгивание с хоста на хост через SSH не составляет труда. В данном сценарии мы проксируем наш трафик через два хоста, намереваясь добраться до искомого хоста, куда не достучаться с нашего актуального хоста campfire.int. Цепочка переходов будет выглядеть так: campfire.int -> vuln-server.int -> internal-web.int -> dns.int.
Это делается при помощи команды ssh -J [email protected],[email protected] [email protected]. Обратите внимание: переходы разделяются запятыми.

Агентская переадресация (-А)
Агентская переадресация по SSH — интересная концепция, о которой подробно рассказано в статье Zero Effort Private Key Compromise: Abusing SSH-Agent for Lateral Movement. Если вы планируете заниматься агентской переадресацией, то настоятельно рекомендую её прочитать. Вкратце: SSH-агент позволяет добавлять приватные ключи/идентификаторы к агенту, работающему на вашей локальной машине. Это делается командой ssh-add <private_key_file>. Список этих ключей выводится командой ssh-add -l. Добавив ключ в утилиту ssh-agent, можно по SSH попасть на сервер, воспользовавшись одним лишь ключом, не вводя пароль повторно. Это удобно при работе как с пользовательскими, так и с сервисными аккаунтами. При помощи опции -A можно переадресовать ваш агент для работы с ключами на ту машину, к которой вы подключаетесь. Так вы сможете пользоваться вашими приватными ключами с той машины, к которой подключаетесь.
В качестве демонстрации давайте предположим, что хотим перепрыгнуть через vuln-server.int на internal-web.int, в то же время переадресуя ключи в нашем ssh-агенте, так, чтобы можно было ими пользоваться, оказавшись на internal-web.int.
Это делается при помощи следующей команды: ssh -A -J [email protected] [email protected]
-
‑A приказывает SSH переадресовывать ключи в нашем SSH‑агенте на удалённую машину internal‑web.int
-
‑J [email protected] приказывает SSH проксировать наш трафик через vuln‑server.int перед тем, как обращаться к internal‑web.int
-
[email protected]: так мы сообщаем SSH, что хотим зайти под именем и паролем на удалённый сервер с правами администратора, что позволит нам открыть SSH‑туннель.
Как видите, выполнив ssh -A -J [email protected] [email protected], можно пользоваться ssh [email protected], и при этом не требуется указывать ни приватный ключ, ни какие-либо учётные данные. Дело в том, что на нашей локальной машине campfire.int есть ssh-ключ для dns.int, загруженный в ssh-агент. Чтобы убедиться в этом, запустим ssh-agent -l.
Выделение TTY-команд (-t)
Этот вариант проще некуда, однако он очень пригодится, если требуется быстро выполнять на удалённом сервере такие команды, которые предполагают те или иные взаимодействия — например, Vim или top. Мой любимый пример такого рода — случай, когда требуется быстро отредактировать файл на удалённом сервере. Для этого нужно всего лишь выполнить команду ssh [email protected] -t top — вы получите приветствие TTY, в котором будет команда top.

Глобальный порт (-g)
Такой вариант встречается реже, но с его помощью можно определить локально переадресуемый порт как «глобальный» (это моя терминология, а не официальная). Тогда мы сможем проксировать и направлять на порт внешнего сервера весь трафик, поступающий на указанный порт нашей локальной машины. Этот вариант напоминает опцию -L, упоминавшуюся выше, но далее мы получаем доступ через оболочку к vuln-server.int и попробуем проксировать любые соединения, идущие на порт 2222, перенаправляя их на порт 22 машины internal-web.int.
Команда для этого выглядит так: ssh -N -f -g -L 2222:localhost:22 [email protected]
-
‑N: так мы сообщаем SSH, что за пределы сервера мы никакие команды отправлять не будем. Без этого мы бы получили оболочку на [email protected]
-
‑f: так мы переводим работу SSH в фоновый режим. Если бы мы так не сделали, то окно терминала зависло бы, и пользоваться терминалом мы бы не смогли.
-
‑g так мы приказываем SSH разрешить удалённым хостам подключаться к локальным переадресуемым портам
-
‑L приказывает SSH переадресовать локальный порт
Как видите, пусть даже наша исходная команда SSH была адресована на порт 2222 машины vuln-server.int, теперь оболочка сообщает нам, что на самом деле мы находимся на internal-web.int, поскольку ssh -N -f -g -L 2222:localhost:22 [email protected]

SSH-консоль (~?)
SSH-консоль — это «скрытая» фича SSH, предоставляющая вам некоторый контроль над SSH, причём, для этого вам не придётся взаимодействовать с удалённой системой. Это полезно, если вы пытаетесь управлять SSH как таковым, но при этом ваша оболочка неисправна. Чтобы вывести меню справки по консоли, нажмите ~?. Если вы умеете работать с vim, то эта комбинация может напомнить вам ведущий символ. Итак, таким образом открывается справочная консоль. Две из имеющихся в ней опций я нахожу очень полезными. Во-первых, это ~, убивающая ваш сеанс (очень кстати, если вы что-то поломали).

Вторая опция — это «Консоль», активируется командой ~C. В ней предусмотрено несколько вариантов переадресации. Если вы зашли на сервер по SSH и решили, что эту сессию будете использовать для проброса портов (например, динамического проброса с применением опции -D, которую мы обсуждали выше), то переадресовать эту сессию можете при помощи опции -D 8080. Тогда данные этой сессии будут пробрасываться на лету. Так, я подключился к vuln-server.int через обычный ssh. Нажал ~C, набрал -D 8080, после чего дважды нажал «ввод» — в ответ мне открылось обычное приглашение командной строки. Но, если мы используем прокси-цепочки на хост-машине campfire.int (и удостоверились, что файл /etc/proxychains.conf настроен для работы с портом 8080), то сессию SSH можно задействовать так, как будто она была инциирована при помощи ssh -D. Ловко.

Конфигурационный файл SSH
Файл с конфигурацией SSH находится по адресу ~/.ssh/config и помогает экономить время, когда вы устанавливаете соединения по SSH. Используемый в нём синтаксис очень легко прослеживается. Кроме того, конфигурацию SSH можно сохранять, чтобы не приходилось заново вводить в командную строку все нужные опции при каждой новой сессии. При установлении соединения SSH выполняет синтаксический разбор этого файла. Если конфигурация того сервера, к которому вы подключаетесь, определена в файле ~/.ssh/config, то именно она и будет использоваться. Обратите внимание: параметры, указанные в командной строке, имеют приоритет над прописанными в конфигурационном файле. Таким образом, если у вас в файле ~/.ssh/config написано, что пользователь internal-web.int должен обладать правами администратора (root), но вы выполняете команду SSH ssh [email protected], SSH попытается запустить вас в систему как graham, а не как root. Ниже в качестве примера приведён элементарный файл ~/.ssh/config.
# Комментарии, начинающиеся с `#`, можно ставить только в начале строки. host internal-web.int User root IdentityFile /home/smores/ssh_agent/internal-web-no-pw Port 2222
Вот как SSH будет разбирать этот файл, выполняя команду ssh internal-web.int:
-
Пытается сопоставить internal‑web.int, фигурирующий в вызове командной строки, с ключевым словом host, сравнивая это значение с internal‑web.int, указанным в ~/.ssh/config
-
Если удаётся найти internal‑web.int в ~/.ssh/config, то будут задействованы все опции, прописанные под хостом и при этом не указанные при вызове в командной строке
-
Если совпадений не найдено, то SSH будет использовать лишь те опции, что вы сами указали в командной строке при вызове SSH.
Ключевые слова для конфигурирования SSH
Есть множество ключевых слов, которые можно задействовать в конфигурационном файле SSH. Но некоторые из тех, которыми я часто пользуюсь, не самоочевидны (например, Port и User) .
IdentityFile /path/to/private_key: позволяет указать приватный ключ, который вы собираетесь использовать на хосте. Семантически равноценно ‑i.

ForwardAgent: то же самое, что выполнить ssh ‑A (опять же, прежде, чем этим пользоваться, почитайте статью Zero Effort Private Key Compromise: Abusing SSH‑Agent for Lateral Movement.)

ProxyJump [email protected]: указываем сервер, через который будет проксироваться трафик. Семантически равноценно опции ‑J, которую мы упоминали выше. Обратите внимание, что в следующем примере требуется аутентифицироваться с адресом [email protected]. Это показывает, что наш трафик действительно маршрутизируется через vuln‑server.int, прежде, чем мы получим оболочку на internal‑web.int, как и просили в команде SSH.

Match: это немного сложнее. Ключевое слово Match предназначено для определения условий в конфигурации SSH. В следующем примере SSH выполняет команду export | grep PROXYME=TRUE. Если программа возвращается с кодом состояния 0 (в данном случае это означает, что grep нашла совпадение), то будет использовать те ключевые слова SSH, которые были определены в блоке Match (здесь — ProxyJump). В противном случае будет использоваться лишь обычный блок host internal‑web.int.
В следующем примере мы сначала выполняем команду ssh internal-web.int, при помощи которой успешно подключаемся к серверу, воспользовавшись при этом приватным ключом, который обозначается ключевым словом IdentityFile. Поскольку export | grep PROXYME=TRUE возвращается с кодом состояния 1 (это означает, что grep не нашла совпадений), мы не выполняем ключевого слова ProxyJump, находящегося в инструкции match.
Далее устанавливаем переменную окружения PROXYME в значение TRUE, воспользовавшись export PROXYME=TRUE, и повторно выполняем всё ту же ssh internal-web.int. На этот раз нам поступает требование аутентифицироваться на time vuln-server.int перед тем, как мы получим оболочку на internal-web.int. Дело в том, что SSH вычислила блок Match и выполнила export | grep PROXYME=TRUE, в ответ на что получила код состояния 0 (это означает, что grep нашла совпадение). Поскольку команда вернула true, она выполнила тот вариант кода, в котором фигурирует ключевое слово ProxyJump, определённое в блоке Match.

Также следует указать, что scp (и некоторые другие утилиты на основе SSH) могут пользоваться вашим конфигурационным файлом SSH! Как правило, это делается по умолчанию, но мне известны случаи, когда это не так. Если вы работаете в такой системе, где scp не использует ваш конфигурационный файл ~/.ssh/config автоматически, то можно явно определить это при помощи аргумента -F ~/.ssh/config.

ssh-copy-id
Утилита ssh-copy-id — это небольшой инструмент, при помощи которого удобно быстро загружать публичный ключ на сервер.
Вот команда для этого: ssh-copy-id -i internal-web [email protected]
-
‑i internal‑web: указываем имя того приватного ключа, при помощи которого мы хотим аутентифицироваться на сервере.
-
[email protected] указываем тот сервер, на который мы хотим загрузить публичный ключ.

ssh-keygen
При помощи этой утилиты генерируются пары, в каждую из которых входят публичный и приватный ключ. Обычно рекомендуется задавать ключ побольше при помощи опции -b. Хотя я и не эксперт по криптографии, обычно чем длиннее — тем лучше, и по умолчанию размер ключа равен 3072 (как минимум, на моей машине). По умолчанию ssh-keygen использует алгоритм RSA, но можно использовать и другие (более предпочтительные и сильные) алгоритмы, указав флаг -t. IE ssh-keygen -t ecdsa -b 521 Кроме того, можно проверять ключи, просматривать их фингерпринт и размер в байтах при помощи команды ssh-keygen -lf <file-name>.

Заключение
Вот и всё, вы познакомились с моей личной шпаргалкой по работе с SSH. Теперь беритесь за дело и заходите по SSH на новые машины, уверенно понимая, что делаете.
Ссылки
https://github.com/cwolff411/redteamvillage-sshtunnels
https://www.ssh.com/academy/ssh/tunneling-example
https://goteleport.com/blog/ssh-tunneling-explained/
ссылка на оригинал статьи https://habr.com/ru/articles/1037106/