From c5094571f87bc05722789e1e6ac990f9a3b07ccd Mon Sep 17 00:00:00 2001 From: Yaroslav Buchnev Date: Wed, 10 May 2017 18:44:01 +0300 Subject: [PATCH] Added eng documentation. --- docs/en/README.md | 109 +++++++++++++++++++++++ docs/en/plugins/addheadermultiline.md | 25 ++++++ docs/en/plugins/addheaderredefinition.md | 65 ++++++++++++++ docs/en/plugins/hostspoofing.md | 31 +++++++ docs/en/plugins/httpsplitting.md | 51 +++++++++++ docs/en/plugins/origins.md | 30 +++++++ docs/en/plugins/ssrf.md | 60 +++++++++++++ docs/en/plugins/validreferers.md | 22 +++++ docs/ru/plugins/ssrf.md | 4 +- docs/ru/plugins/validreferers.md | 2 +- 10 files changed, 396 insertions(+), 3 deletions(-) create mode 100644 docs/en/README.md create mode 100644 docs/en/plugins/addheadermultiline.md create mode 100644 docs/en/plugins/addheaderredefinition.md create mode 100644 docs/en/plugins/hostspoofing.md create mode 100644 docs/en/plugins/httpsplitting.md create mode 100644 docs/en/plugins/origins.md create mode 100644 docs/en/plugins/ssrf.md create mode 100644 docs/en/plugins/validreferers.md diff --git a/docs/en/README.md b/docs/en/README.md new file mode 100644 index 0000000..9e9c23d --- /dev/null +++ b/docs/en/README.md @@ -0,0 +1,109 @@ +Gixy is a static analysis tool for Ngix config files. It is best at finding security misconfigurations, but can be also used to detect general errors. + +## What it can do +Right now Gixy can find: + * [[ssrf] Server Side Request Forgery](https://github.com/yandex/gixy/blob/master/docs/en/plugins/ssrf.md) + * [[http_splitting] HTTP Splitting](https://github.com/yandex/gixy/blob/master/docs/en/plugins/httpsplitting.md) + * [[origins] Problems with referrer/origin validation](https://github.com/yandex/gixy/blob/master/docs/en/plugins/origins.md) + * [[add_header_redefinition] Redefining of upstream response headers with directive "add_header"](https://github.com/yandex/gixy/blob/master/docs/en/plugins/addheaderredefinition.md) + * [[host_spoofing] Request's Host header forgery](https://github.com/yandex/gixy/blob/master/docs/en/plugins/hostspoofing.md) + * [[valid_referers] none in valid_referers](https://github.com/yandex/gixy/blob/master/docs/en/plugins/validreferers.md) + * [[add_header_multiline] Multiline response headers](https://github.com/yandex/gixy/blob/master/docs/en/plugins/addheadermultiline.md) + +You can find things that Gixy is learning to detect at [Issues labeled with "new plugin"](https://github.com/yandex/gixy/issues?q=is%3Aissue+is%3Aopen+label%3A%22new+plugin%22) + +##Installation +The easiest way to install Gixy is to use pip: +```bash +pip install gixy +``` +The tool is compatible with Python 2.7/3.5/3.6 + +## Using Gixy +After installation you can find `gixy` in your command line. Buy default Gixy is working with `/etc/nginx/nginx.conf` folder, which is standart for Nginx installations, but you can provide a custom path as an argument: +``` +$ gixy /www/configs/nginx/nginx.conf +``` + +==================== Results =================== + +Problem: [http_splitting] Possible HTTP-Splitting vulnerability. +Description: Using variables that can contain "\n" may lead to http injection. +Additional info: https://github.com/yandex/gixy/wiki/ru/httpsplitting +Reason: At least variable "$action" can contain "\n" +Pseudo config: +include /etc/nginx/sites/default.conf; + + server { + + location ~ /v1/((?[^.]*)\.json)?$ { + add_header X-Action $action; + } + } + + +==================== Summary =================== +Total issues: + Unspecified: 0 + Low: 0 + Medium: 0 + High: 1 +``` +Gixy can process `include` directive and tries to handle all the dependencies. If something went wrong, you can launch Gixy with the `d` flag, which enables debug mode for extra information. + +To view all options: +``` +$ gixy -h +usage: gixy [-h] [-c CONFIG_FILE] [--write-config CONFIG_OUTPUT_PATH] + [-v] [-l] [-f {console,text,json}] [-o OUTPUT_FILE] [-d] + [--tests TESTS] [--skips SKIPS] [--disable-includes] + [--origins-domains domains] + [--origins-https-only https_only] + [--add-header-redefinition-headers headers] + [nginx.conf] + +Gixy - a Nginx configuration [sec]analyzer + +positional arguments: + nginx.conf Path to nginx.conf, e.g. /etc/nginx/nginx.conf + +optional arguments: + -h, --help show this help message and exit + -c CONFIG_FILE, --config CONFIG_FILE + config file path + --write-config CONFIG_OUTPUT_PATH + takes the current command line args and writes them + out to a config file at the given path, then exits + -v, --version show program's version number and exit + -l, --level Report issues of a given severity level or higher (-l + for LOW, -ll for MEDIUM, -lll for HIGH) + -f {console,text,json}, --format {console,text,json} + Specify output format + -o OUTPUT_FILE, --output OUTPUT_FILE + Write report to file + -d, --debug Turn on debug mode + --tests TESTS Comma-separated list of tests to run + --skips SKIPS Comma-separated list of tests to skip + --disable-includes Disable "include" directive processing + +plugins options: + --origins-domains domains + Default: * + --origins-https-only https_only + Default: False + --add-header-redefinition-headers headers + Default: content-security-policy,x-xss- + protection,x-frame-options,x-content-type- + options,strict-transport-security,cache-control + + +available plugins: + host_spoofing + add_header_multiline + http_splitting + valid_referers + origins + add_header_redefinition + ssrf +``` + diff --git a/docs/en/plugins/addheadermultiline.md b/docs/en/plugins/addheadermultiline.md new file mode 100644 index 0000000..f532f62 --- /dev/null +++ b/docs/en/plugins/addheadermultiline.md @@ -0,0 +1,25 @@ +# [add_header_multiline] Multiline response headers + +You should avoid using multiline response headers, because: + * they sre considered depricated (см. [RFC 7230](https://tools.ietf.org/html/rfc7230#section-3.2.4)); + * Some HTTP-clients and web browser never supported them (e.g. IE/Edge/Nginx). + +## How can I find it? +Misconfiguration example: +```nginx +# http://nginx.org/ru/docs/http/ngx_http_headers_module.html#add_header +add_header Content-Security-Policy " + default-src: 'none'; + script-src data: https://yastatic.net; + style-src data: https://yastatic.net; + img-src data: https://yastatic.net; + font-src data: https://yastatic.net;"; + +# https://www.nginx.com/resources/wiki/modules/headers_more/ +more_set_headers -t 'text/html text/plain' + 'X-Foo: Bar + multiline'; +``` + +## What can I do? +The only solution is to never use multiline response headers. \ No newline at end of file diff --git a/docs/en/plugins/addheaderredefinition.md b/docs/en/plugins/addheaderredefinition.md new file mode 100644 index 0000000..0ce8b22 --- /dev/null +++ b/docs/en/plugins/addheaderredefinition.md @@ -0,0 +1,65 @@ +# [add_header_redefinition] Redefining of upstream response headers with directive "add_header" + +Unfortunately, many people consider the use of `add_header` directive for headers redefining a good practice. +This approach is flawed, which is discussed in Nginx [docs](http://nginx.org/ru/docs/http/ngx_http_headers_module.html#add_header): +> There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level. + +The logic is quite simple: if you set headers at one level (for example, in `server` section) and then at a lower level (let's say `location`) you set some other headers, then the first group won't apply. + +It's easy to observe: + - Configuration: +```nginx +server { + listen 80; + add_header X-Frame-Options "DENY" always; + location / { + return 200 "index"; + } + + location /new-headers { + # Add special cache control + add_header Cache-Control "no-cache, no-store, max-age=0, must-revalidate" always; + add_header Pragma "no-cache" always; + + return 200 "new-headers"; + } +} +``` + - Location request `/` (`X-Frame-Options` header is in server response): +```http +GET / HTTP/1.0 + +HTTP/1.1 200 OK +Server: nginx/1.10.2 +Date: Mon, 09 Jan 2017 19:28:33 GMT +Content-Type: application/octet-stream +Content-Length: 5 +Connection: close +X-Frame-Options: DENY + +index +``` + - Location request `/new-headers` (headers `Cache-Control` and `Pragma` are present, but there's no `X-Frame-Options`): +```http +GET /new-headers HTTP/1.0 + + +HTTP/1.1 200 OK +Server: nginx/1.10.2 +Date: Mon, 09 Jan 2017 19:29:46 GMT +Content-Type: application/octet-stream +Content-Length: 11 +Connection: close +Cache-Control: no-cache, no-store, max-age=0, must-revalidate +Pragma: no-cache + +new-headers +``` + +## What can I do? +There are several ways to solve this problem: + - dublicate important headers; + - set all headers at one level (`server` section is a good choice) + - use [ngx_headers_more](https://www.nginx.com/resources/wiki/modules/headers_more/) module. + +No solution is perfect, so choose one based on your needs. \ No newline at end of file diff --git a/docs/en/plugins/hostspoofing.md b/docs/en/plugins/hostspoofing.md new file mode 100644 index 0000000..9edc1c4 --- /dev/null +++ b/docs/en/plugins/hostspoofing.md @@ -0,0 +1,31 @@ +# [host_spoofing] Request's Host header forgery + +Often, an application located behind Nginx needs a correct `Host` header for URL generation (redirects, resources, links in emails etc.). +An attacker can spoof this header, which leads to a variety of problems, from phishing to SSRF. To prevent this, avoid: +> Relience on `X-Forwarded-Host` request header; +> In this case you have to ensure the header is set correctly at proxies; + +## How can I find it? +Most of the time it's a result of using `$http_host` variable instead of `$host`. + +And they are quite different: + * `$http` - host in order of priority: host name from request string, host name form `Host` request header, or a server name, compliant to the request; + * `$http_host` - "Host" request header. + +Config sample: +```nginx +location @app { + proxy_set_header Host $http_host; + # Other proxy params + proxy_pass http://backend; +} +``` + +## What can I do? +Luckly, all is quite obvious: + * list all the correct server names in `server name` directive; + * always use `$host` instead of `$http_host`. + +## Additional info + * [Host of Troubles Vulnerabilities](https://hostoftroubles.com/) + * [Practical HTTP Host header attacks](http://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html) \ No newline at end of file diff --git a/docs/en/plugins/httpsplitting.md b/docs/en/plugins/httpsplitting.md new file mode 100644 index 0000000..d0c85a5 --- /dev/null +++ b/docs/en/plugins/httpsplitting.md @@ -0,0 +1,51 @@ +# [http_splitting] HTTP Splitting + +HTTP Splitting - attack that use improper input validation. It usually targets web application located behind Nginx (HTTP Request Splitting) or its users (HTTP Response Splitting). + +Vulnerability is created when an attacker can insert new line symbol "\n" into request or into response, created by Nginx server. + +## How can I find it? +You should always pay attention to: + - variables that are used in directives, responsible for the request creation (for they may contain CRLF), e.g. `rewrite`, `return`, `add_header`, `proxy_set_header` or `proxy_pass`; + - `$uri` and `$document_uri` variables, and the directive that process them, because these variables contain url-encoded value; + - variables, that are selected from an exclusive range, e.g. `(?P[^.]+)`. + + +An example of configuration that contains vriable, selected from an exclusive range: +```nginx +server { + listen 80 default; + + location ~ /v1/((?[^.]*)\.json)?$ { + add_header X-Action $action; + return 200 "OK"; + } +} +``` + +Explotation: +```http +GET /v1/see%20below%0d%0ax-crlf-header:injected.json HTTP/1.0 +Host: localhost + +HTTP/1.1 200 OK +Server: nginx/1.11.10 +Date: Mon, 13 Mar 2017 21:21:29 GMT +Content-Type: application/octet-stream +Content-Length: 2 +Connection: close +X-Action: see below +x-crlf-header:injected + +OK +``` +As you can see, an attacker could add `x-crlf-header: injected` response header. This was possible because: + - `add_header` doesn't encode or validate input value on suggestion that author knows about the consiquences; + - the path value is normalized before processing of the location; + - `$action` value was given from a regexp with an exclusive range: `[^.]*`; + - as the result, `$action` value is equal to that of `see below\r\nx-crlf-header:injected` and on its use the header was added to the response. + +## What can I do? + - try to use safe variables, e.g. `$request_uri` instead of `$uri`; + - forbid the use of the new line symbol in the exclusive range by using `/some/(?[^/\s]+)` instead of `/some/(?[^/]+` + - it could be a good idea to validate `$uri` (only if you're sure you know what are you getting into). diff --git a/docs/en/plugins/origins.md b/docs/en/plugins/origins.md new file mode 100644 index 0000000..62e4ce2 --- /dev/null +++ b/docs/en/plugins/origins.md @@ -0,0 +1,30 @@ +# [origins] Problems with referrer/origin validation + +It's not unusual to use regexp for `Referer` or `Origin` headers validation. +Often it is needed for setting the `X-Frame-Options` header (ClickJacking protection) or Cross-Origin Resource Sharing. + +The most common errors with this configuration are: + - regexp errors; + - 3rd-party domain permissions. + + > By default Gixy doesn't check regexps for 3rd-party domains matching, cause it's unclear wether you can should them. You can pass a list of trusted domains by using the option `--origins-domains example.com,foo.bar` + +## How can I find it? +"Eazy"-breezy: + - you have to find all the `if` directives that are in charge of `$http_origin` or `$http_referer` check; + - make sure your regexps are a-ok. + +Misconfig example: +```nginx +if ($http_origin ~* ((^https://www\.yandex\.ru)|(^https://ya\.ru)/)) { + add_header 'Access-Control-Allow-Origin' "$http_origin"; + add_header 'Access-Control-Allow-Credentials' 'true'; +} +``` + +TODO(buglloc): cover typical regexp-writing problems +TODO(buglloc): Regex Ninja? + +## What can I do? +Fix your regexp or toss it away. +If you use regexp validation for `Referer` request header, then, possibly (not 100%), you could use [ngx_http_referer_module](http://nginx.org/ru/docs/http/ngx_http_referer_module.html). \ No newline at end of file diff --git a/docs/en/plugins/ssrf.md b/docs/en/plugins/ssrf.md new file mode 100644 index 0000000..2cb6a7b --- /dev/null +++ b/docs/en/plugins/ssrf.md @@ -0,0 +1,60 @@ +# [ssrf] Server Side Request Forgery + +Server Side Request Forgery - attack that forces a server to perform requests on behalf of an attacker (Nginx in our case). +It's possible when an attacker controls the address of a proxied server (second argument of the `proxy_pass` directive). + + +## How can I find it? +There are two types of errors that make a server vulnerable: + - lack of the [internal](http://nginx.org/ru/docs/http/ngx_http_core_module.html#internal) directive. It is used to point out a location that can be used for internal requests only; + - unsafe internal redirection. + +### Lack of the internal directive +Classical misconfig, based on lack of the internal directive, that makes SSRF possible: +```nginx +location ~ /proxy/(.*)/(.*)/(.*)$ { + proxy_pass $1://$2/$3; +} +``` +An attacker has complete control over the proxied address, that makes sending requests on behalf of Nginx possible. + +### Unsafe internal redirection +Let's say you have internal location in your config and that location uses some request data as proxied server's address. + +E.g.: +```nginx +location ~* ^/internal-proxy/(?https?)/(?.*?)/(?.*)$ { + internal; + + proxy_pass $proxy_proto://$proxy_host/$proxy_path ; + proxy_set_header Host $proxy_host; +} +``` + +According to Nginx docs, internal requests are: +> - requests redirected by the **error_page**, **index**, **random_index**, and **try_files** directives; +> - requests redirected by the “X-Accel-Redirect” response header field from an upstream server; +> - subrequests formed by the “include virtual” command of the ngx_http_ssi_module module and by the ngx_http_addition_module module directives; +> - requests changed by the **rewrite** directive.]> + +Accordingly, any unsafe rewrite allows an attacker to make an internal request and control a proxied server's address. + +Misconfig example: +```nginx +rewrite ^/(.*)/some$ /$1/ last; + +location ~* ^/internal-proxy/(?https?)/(?.*?)/(?.*)$ { + internal; + + proxy_pass $proxy_proto://$proxy_host/$proxy_path ; + proxy_set_header Host $proxy_host; +} +``` + +## What can I do? +There are everal rules you better follow when writing such configurations: + - use only `internal location` for proxying; + - if possible, forbid user data transmission; + - protect proxied server's address: + * if the quantity of proxied hosts is limited (when you have S3 or smth), you better hardcode them and choose them with `map` or do it some other way; + * if you can' list all possible hosts to proxy, you should sign the address. diff --git a/docs/en/plugins/validreferers.md b/docs/en/plugins/validreferers.md new file mode 100644 index 0000000..bff98e0 --- /dev/null +++ b/docs/en/plugins/validreferers.md @@ -0,0 +1,22 @@ +# [valid_referers] none in valid_referers +Module [ngx_http_referer_module](http://nginx.org/ru/docs/http/ngx_http_referer_module.html) allows to block the access to service for requests with wrong `Referer~ value. +It's often used for setting `X-Frame-Options` header (ClickJacking protection), but there may be other cases. + +Typical problems with this module's config: + * use of `server_names` with bad server name (`server_name` directive); + * too broad and/or bad regexps; + * use of `none`. + +> At the moment, Gixy can only detect the use of `none` as a valid referer. + +## Why none is bad? +According to [docs](http://nginx.org/ru/docs/http/ngx_http_referer_module.html#valid_referers): +> `none` - the “Referer” field is missing in the request header; + +Still, it's important to remember that any resource can make user's browser to make a request without a `Referer` request header. +E.g.: + - in case of redirect from HTTP to HTTPS; + - by setting up a properc[Referrer Policy](https://www.w3.org/TR/referrer-policy/); + - a request with opaque origin, `data:` scheme, for example. + +So, by using `none` as a valid referer, you nullify any attemps in refferer validation. \ No newline at end of file diff --git a/docs/ru/plugins/ssrf.md b/docs/ru/plugins/ssrf.md index b3466c6..8519577 100644 --- a/docs/ru/plugins/ssrf.md +++ b/docs/ru/plugins/ssrf.md @@ -32,12 +32,12 @@ location ~* ^/internal-proxy/(?https?)/(?.*?)/(? - запросы, перенаправленные директивами **error_page**, index, random_index и **try_files**; +> - запросы, перенаправленные директивами **error_page**, **index**, **random_index** и **try_files**; > - запросы, перенаправленные с помощью поля “X-Accel-Redirect” заголовка ответа вышестоящего сервера; > - подзапросы, формируемые командой “include virtual” модуля ngx_http_ssi_module и директивами модуля ngx_http_addition_module; > - запросы, изменённые директивой **rewrite**.]> -Соответственно, любой "не осторожный" реврайт позволит злоумышленнику сделать внутренний запрос и контролировать адрес проксируемого сервера. +Соответственно, любой "неосторожный" реврайт позволит злоумышленнику сделать внутренний запрос и контролировать адрес проксируемого сервера. Пример плохой конфигурации: ```nginx diff --git a/docs/ru/plugins/validreferers.md b/docs/ru/plugins/validreferers.md index 29130af..0200a80 100644 --- a/docs/ru/plugins/validreferers.md +++ b/docs/ru/plugins/validreferers.md @@ -19,4 +19,4 @@ - указав соответствующую [Referrer Policy](https://www.w3.org/TR/referrer-policy/); - обращение с opaque origin, например, используя схему `data:`. -Таким образом, используя `none` в качестве валидного реферера вы сводиде на нет любые попытки валидации реферера. \ No newline at end of file +Таким образом, используя `none` в качестве валидного реферера вы сводите на нет любые попытки валидации реферера. \ No newline at end of file