Nginx -наиболее применяемый веб-сервер в Интернете, потому что он модульный, быстро реагирует под нагрузкой и может масштабироваться до минимального оборудования. Detectify регулярно сканирует Nginx на предмет неправильных настроек и уязвимостей, которые могут нанести вред пользователям. Обнаруженные уязвимости затем вводятся в качестве теста безопасности в сканер веб-приложений.
Nginx — это веб-сервер, на котором работает треть всех сайтов в мире. Но если забыть или проигнорировать некоторые ошибки в настройках, можно стать отличной мишенью для злоумышленников. Detectify Crowdsource подготовил список наиболее часто встречающихся ошибок, делающих сайт уязвимым для атак.
Мы изучили почти 50 000 уникальных файлов конфигурации Nginx, загруженных с GitHub с помощью Google BigQuery. С помощью собранных данных удалось выяснить, какие ошибки конфигурации встречаются чаще всего. В этой статье будут разъяснены следующие неправильные настройки в Nginx:
-
Отсутствует корневой каталог
-
Небезопасное использование переменных
-
Чтение необработанного ответа сервера
-
merge_slashes отключены
Нет корневого каталога
server {
root /etc/nginx;
location /hello.txt {
try_files $uri $uri/ =404;
proxy_pass http://127.0.0.1:8080/;
}
}
Корневую папку Nginx указывает Root-директива. В приведенном выше примере корневая папка — / etc / nginx
, что означает, что мы можем получить доступ к файлам в этой папке. В приведенной выше конфигурации нет места для / (location / {...}
), только для /hello.txt.
Это установит корневую директиву глобально, что означает, что запросы к /
перенаправят вас на локальный путь / etc / nginx.
Простой запрос типа GET /nginx.conf
откроет содержимое файла конфигурации Nginx, хранящегося в /etc/nginx/nginx.conf
. Если root установлен на / etc
, запрос GET к / nginx / nginx.conf
покажет файл конфигурации. В некоторых случаях можно получить доступ к другим файлам конфигурации, журналам доступа или даже зашифрованным учетным данным для базовой аутентификации HTTP.
Из почти 50 000 файлов конфигурации Nginx, которые мы проанализировали, наиболее распространёнными корневыми путями были следующие:
Слеш который потерялся
server {
listen 80 default_server;
server_name _;
location /static {
alias /usr/share/nginx/static/;
}
location /api {
proxy_pass http://apiserver/v1/;
}
}
При неверной настройке off-by-slash есть возможность перейти на один шаг вверх по пути из-за отсутствия косой черты. Orange Tsai поделился этой информацией в своем выступлении на Blackhat «Breaking Parser Logic!». Он показал, как отсутствие завершающей косой черты в директиве Location в сочетании с директивой Alias
позволяет читать исходный код веб-приложения. Менее известно то, что это также работает с другими директивами, такими как proxy_pass
. Давайте посмотрим, что происходит и почему это работает.
location /api {
proxy_pass http://apiserver/v1/;
}
Если на Nginx запущена следующая конфигурация, доступная на сервере, можно предположить, что доступны только пути в http://apiserver/v1/
.
http://server/api/user -> http://apiserver/v1//user
Когда запрашивается http: // server / api / user
, Nginx сначала нормализует URL. Затем он проверяет, соответствует ли префикс / api
URL-адресу, что он и делает в этом случае. Затем префикс удаляется из URL-адреса, оставляя путь /user
. Затем этот путь добавляется к URL-адресу proxy_pass
, в результате чего получается конечный URL-адрес http: // apiserver / v1 // user.
Обратите внимание, что в URL-адресе есть двойная косая черта, поскольку директива местоположения не заканчивается косой чертой, а путь URL-адреса proxy_pass
заканчивается косой чертой. Большинство веб-серверов нормализуют http: // apiserver / v1 // user
до http: // apiserver / v1 / user
, что означает, что даже с этой неправильной конфигурацией все будет работать должным образом и может остаться незамеченным.
Данная неверная конфигурация может быть использована путем запроса http: //server/api../
, в результате чего Nginx запрашивает URL-адрес http: //apiserver/v1/../,
который нормализуется до http: // apiserver /
. Уровень вреда от такой ошибки определяется тем, чего можно достичь, если использовать эту неправильную конфигурацию. Например, это может привести к отображению статуса сервера Apache с URL-адресом http: //server/api../server-status
или сделать доступными пути, которые не должны быть общедоступными.
Одним из признаков того, что сервер Nginx неправильно настроен, является то, что сервер возвращает тот же ответ при удалении косой черты из URL-адреса. То есть, если http: // server / api / user
и http: // server / apiuser
возвращают один и тот же ответ, сервер может быть уязвимым. Он позволяет отправлять следующие запросы:
http://server/api/user -> http://apiserver/v1//user
http://server/apiuser -> http://apiserver/v1/user
Опасное применение переменных
Кое какие фреймворки, сценарии и конфигурации Nginx небезопасно используют переменные, хранящиеся в Nginx. Это может привести к таким проблемам, как XSS, обход HttpOnly, раскрытие информации и, в некоторых случаях, даже RCE.
С такой конфигурацией, как эта:
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
}
основная проблема будет заключаться в том, что Nginx отправит интерпретатору PHP любой URL-адрес, заканчивающийся на .php, даже если файл не существует на диске. Это распространённая ошибка во многих конфигурациях Nginx, и об этом говорится в документе «Ловушки и распространенные ошибки», созданном Nginx.
XSS возможен, если PHP-скрипт попытается определить базовый URL на основе SCRIPT_NAME
;
<?php
if(basename($_SERVER['SCRIPT_NAME']) ==
basename($_SERVER['SCRIPT_FILENAME']))
echo dirname($_SERVER['SCRIPT_NAME']);
?>
GET /index.php/<script>alert(1)</script>/index.php
SCRIPT_NAME = /index.php/<script>alert(1)</script>/index.php
Применение $uri может послужить причиной CRLF-инъекции
Другая неправильная конфигурация, связанная с переменными Nginx, заключается в использовании $uri
или $document_uri
вместо $request_uri
.
$ur
i и $document_uri
содержат нормализованный URI, тогда как нормализация в Nginx включает URL-декодирование URI. В блоге Volema рассказывалось, что $uri обычно используется при создании перенаправлений в конфигурации Nginx, что приводит к внедрению CRLF.
Пример уязвимой конфигурации Nginx:
location / {
return 302 https://example.com$uri;
}
Символами новой строки для HTTP-запросов являются \ r (возврат каретки) и \ n (перевод строки). URL-кодирование разрывов строк приводит к следующему символьному представлению %0d%0a
. Когда эти символы появляются в запросе типа http: // localhost /
Detectify: clrf
на неправильно настроенном сервере сервер отвечает новым заголовком с именем Detectify, потому что переменная $uri
содержит новые строчные буквы, декодированные в URL.
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.19.3
Content-Type: text/html
Content-Length: 145
Connection: keep-alive
Location: https://example.com/
Detectify: clrf
Произвольные переменные
В определенных вариантах данные, предоставленные пользователем, можно рассматривать как переменную Nginx. Непонятно, почему это происходит, но это не такая уж и редкость. Это довольно сложно проверяется, как показывает этот отчет. Если мы посмотрим на сообщение об ошибке, то увидим, что оно находится в модуле фильтра SSI, то есть связано с SSI.
Одним из способов проверки является установка значения заголовка referer:
$ curl -H ‘Referer: bar’ http://localhost/foo$http_referer | grep ‘foobar’
Мы просканировали эту неправильную конфигурацию и обнаружили несколько случаев, когда пользователь мог получить значение переменных Nginx. Количество обнаруженных уязвимых экземпляров уменьшилось, что может указывать на то, что уязвимость исправлена.
Чтение необработанного ответа сервера
С помощью proxy_pass
Nginx можно перехватывать ошибки и заголовки HTTP, генерируемые серверной частью (backend). Это очень полезно, если вы хотите скрыть внутренние сообщения об ошибках и заголовки, чтобы они обрабатывались Nginx. Nginx автоматически предоставит настраиваемую страницу ошибки, если серверная часть на нее ответит. Что происходит, когда Nginx не понимает, что это HTTP-ответ?
Если клиент посылает неправильный HTTP-запрос в Nginx, этот запрос будет перенаправлен на серверную часть как есть и ответит своим необработанным содержимым. Тогда Nginx не распознает недействительный HTTP-ответ и просто отправит его клиенту. Представьте себе приложение uWSGI, подобное этому:
def application(environ, start_response):
start_response('500 Error', [('Content-Type',
'text/html'),('Secret-Header','secret-info')])
return [b"Secret info, should not be visible!"]
И со следующими директивами в Nginx:
http {
error_page 500 /html/error.html;
proxy_intercept_errors on;
proxy_hide_header Secret-Header;
}
proxy_intercept_errors будет обслуживать пользовательский ответ, если бэкенд имеет код ответа больше 300. В нашем приложении uWSGI выше мы отправим ошибку 500, которая будет перехвачена Nginx.
proxy_hide_header почти не требует пояснений; он скроет любой указанный HTTP-заголовок от клиента.
Если мы отправим обычный GET-запрос, Nginx вернёт:
HTTP/1.1 500 Internal Server Error
Server: nginx/1.10.3
Content-Type: text/html
Content-Length: 34
Connection: close
Но если мы отправим неверный HTTP-запрос, например:
GET /? XTTP/1.1
Host: 127.0.0.1
Connection: close
То получим такой ответ:
XTTP/1.1 500 Error
Content-Type: text/html
Secret-Header: secret-info
Secret info, should not be visible!
merge_slashes отключены
Для директивы merge_slashes
по умолчанию установлено значение «on», что является механизмом для сжатия двух или более слешей в один, поэтому ///
превратится в /.
Если Nginx используется в качестве обратного прокси-сервера, а приложение-прокси уязвимо для включения локальных файлов, дополнительные слеши в запросе могут оставить место для его использования. Мы нашли 33 файла Nginx с параметром merge_slashes, установленным на «off».
Заключение
Nginx — это очень мощный фреймворк для веб-серверов, и легко понять, почему он широко используется. Но с гибкой настройкой вы даете возможность совершать ошибки, которые могут повлиять на безопасность. Не позволяйте злоумышленнику слишком легко взломать ваш сайт, не проверив эти распространенные ошибки конфигурации.