[PATCH] fix weakness by logging of broken header by incorect proxy protocol (IDS/IPS/LOG-analysis)
Maxim Dounin
mdounin at mdounin.ru
Sun Oct 9 21:54:28 UTC 2022
Hello!
On Wed, Sep 28, 2022 at 02:02:30PM +0400, Roman Arutyunyan wrote:
> Hi Sergey,
>
> On Mon, Sep 26, 2022 at 11:16:05PM +0200, Dipl. Ing. Sergey Brester via nginx-devel wrote:
> >
> >
> > Hi,
> >
> > below is a patch to fix a weakness by logging of broken header by
> > incorrect proxy protocol.
> >
> > If some service (IDS/IPS) analyzing or monitoring log-file, regularly
> > formatted lines may be simply confused with lines written not escaped
> > directly from buffer supplied from foreign source.
> > Not to mention it may open a certain vector allowing "injection" of user
> > input in order to avoid detection of failures or even to simulate
> > malicious traffic from legitimate service.
> >
> > How to reproduce:
> >
> > - enable proxy_protocol for listener and start nginx (here localhost on
> > port 80);
> > - echo 'set s [socket localhost 80]; puts $s "testntestntest"; close $s'
> > | tclsh
> >
> > Error-log before fix:
> >
> > 2022/09/26 19:29:58 [error] 10104#17144: *3 broken header: "test
> > test
> > test
> > " while reading PROXY protocol, client: 127.0.0.1, server: 0.0.0.0:80
> >
> > Error-log after fix:
> >
> > 2022/09/26 22:48:50 [error] 13868#6132: *1 broken header:
> > "test→→test→→test→→" while reading PROXY protocol, client: 127.0.0.1,
> > server: 0.0.0.0:80
> >
> > It is not advisable to log such foreign user input unescaped to the
> > formatted log-file: instead of "...ntestn..." the attacker can write
> > correctly formatted line simulating a 401-, 403-failure or rate-limit
> > overran, so IDS could block a innocent service or mistakenly ban
> > legitimate user.
> >
> > The patch proposes simplest escape (LF/CR-char with →, double quote with
> > single quote and additionally every char larger or equal than 0x80 to
> > avoid possible logging of "broken" utf-8 sequences or unsupported
> > surrogates, just as a safest variant for not-valid foreign buffer)
> > in-place in the malicious buffer directly (without mem-alloc, etc).
> >
> > Real life example -
> > https://github.com/fail2ban/fail2ban/issues/3303#issuecomment-1148691902
>
> Thanks for reporting this. The issue indeed needs to be fixed. Attached is
> a patch similar to yours that does this. I don't think we need to do anything
> beyond just cutting the first line since there's another similar place in
> nginx - ngx_http_log_error_handler(), where exactly that is implemented.
>
> Whether we need to skip special characters when logging to nginx log is
> a topic for a bigger discussion and this will require a much bigger patch.
> I suggest that we only limit user data to the first line now.
>
> [..]
>
> --
> Roman Arutyunyan
> # HG changeset patch
> # User Roman Arutyunyan <arut at nginx.com>
> # Date 1664359213 -14400
> # Wed Sep 28 14:00:13 2022 +0400
> # Node ID 001b2449cfd730fd688a7298458e25113c15a947
> # Parent 615268a957ab930dc4be49fe5f6f88cd7e377f12
> Log only the first line of user input on PROXY protocol v1 error.
>
> Previously, all received user input was logged. If a multi-line text was
> received from client and logged, it could reduce log readability and also make
> it harder to parse nginx log by scripts. The change brings to PROXY protocol
> the same behavior that exists for HTTP request line in
> ngx_http_log_error_handler().
>
> diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c
> --- a/src/core/ngx_proxy_protocol.c
> +++ b/src/core/ngx_proxy_protocol.c
> @@ -185,8 +185,14 @@ skip:
>
> invalid:
>
> + for (p = buf; p != last; p++) {
I would rather prefer "p < last", as in
ngx_http_log_error_handler() and in the skip section of this
function (and many other places).
While there is no real difference, the "p < last" looks more in
line with the existing code, and also slightly safer if due to a
bug elsewhere "buf" will happen to be larger than "last".
> + if (*p == CR || *p == LF) {
> + break;
> + }
> + }
> +
> ngx_log_error(NGX_LOG_ERR, c->log, 0,
> - "broken header: \"%*s\"", (size_t) (last - buf), buf);
> + "broken header: \"%*s\"", (size_t) (p - buf), buf);
>
> return NULL;
> }
Otherwise looks good.
--
Maxim Dounin
http://mdounin.ru/
More information about the nginx-devel
mailing list