Security of binary_to_term ?

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Security of binary_to_term ?

Pascal Brisset
erlang:binary_to_term/1 generally exits with 'badarg' when applied to
invalid inputs. Is this behaviour guaranteed ? In other words, is it
safe to decode untrusted data with binary_to_term ?

The purpose is to send data between untrusted nodes with
term_to_binary and binary_to_term over TCP, rather than with the
erlang distribution protocol.

--- Pascal Brisset <pascal.brisset> +33141986741 --
----- Cellicium | 73 avenue Carnot | 94230 Cachan | France ------



Reply | Threaded
Open this post in threaded view
|

Security of binary_to_term ?

Claes Wikström
On Wed, Jun 27, 2001 at 01:15:21PM +0200, Pascal Brisset wrote:
> erlang:binary_to_term/1 generally exits with 'badarg' when applied to
> invalid inputs. Is this behaviour guaranteed ? In other words, is it
> safe to decode untrusted data with binary_to_term ?
>
> The purpose is to send data between untrusted nodes with
> term_to_binary and binary_to_term over TCP, rather than with the
> erlang distribution protocol.
>


A number of checks are done trying to validate the data, however
I think there are some pathological cases left where the emulator
dies. Think so anyway.

An aside note: If you get the data over TCP, why should it be
invalid. TCP ensures the data is non corrupted.... or maybe you
are worrying over rouge nodes ???

/klacke

--
Claes Wikstrom                        -- Caps lock is nowhere and
Alteon WebSystems                     -- everything is under control          
http://www.bluetail.com/~klacke       --



Reply | Threaded
Open this post in threaded view
|

Security of binary_to_term ?

Pascal Brisset
 > An aside note: If you get the data over TCP, why should it be
 > invalid. TCP ensures the data is non corrupted.... or maybe you
 > are worrying over rouge nodes ???

Well this is what security is about, isn't it ? :) Actually I stumbled
on one of those pathological cases, and I was wondering whether it was
just a bug or whether additional checks were required anyway.

$ erl
Erlang (BEAM) emulator version 5.0.2.4 [source]

Eshell V5.0.2.4  (abort with ^G)
1> binary_to_term(<<131,111,255,0,0,0>>).
zsh: 30198 segmentation fault  ./bin/erl

-- Pascal Brisset <pascal.brisset> +33141986741 --
----- Cellicium | 73 avenue Carnot | 94230 Cachan | France -----



Reply | Threaded
Open this post in threaded view
|

Security of binary_to_term ?

Vance Shipley-2
> 1> binary_to_term(<<131,111,255,0,0,0>>).
> zsh: 30198 segmentation fault  ./bin/erl

Well this isn't right though is it?  I found that I also got
a segmentation fault on Linux but not on WindowsNT.

In any event segmentation faults are never proper behaviour.

        -Vance


Reply | Threaded
Open this post in threaded view
|

Security of binary_to_term ?

Lon Willett
In reply to this post by Pascal Brisset
This stuff looks like it's down my alley, so I'll add my $0.02.

Pascal Brisset <pascal.brisset> writes:

>  > An aside note: If you get the data over TCP, why should it be
>  > invalid. TCP ensures the data is non corrupted.... or maybe you
>  > are worrying over rogue nodes ???
>
> Well this is what security is about, isn't it ? :) Actually I stumbled
> on one of those pathological cases, and I was wondering whether it was
> just a bug or whether additional checks were required anyway.
>
> $ erl
> Erlang (BEAM) emulator version 5.0.2.4 [source]
>
> Eshell V5.0.2.4  (abort with ^G)
> 1> binary_to_term(<<131,111,255,0,0,0>>).
> zsh: 30198 segmentation fault  ./bin/erl

Ugh!  Crashing the emulator is a bad sign.

I wouldn't worry overmuch about the crash per se, since all an
attacker could use that for would be a denial-of-service attack (DOS).
IMO, preventing DOS attacks is probably impossible (although one
shouldn't make them _too_ easy).

What is a concern is that the segfault indicates that there might be a
buffer overflow problem, and this possibly would allow an attacker to
execute arbitrary code on your machine.

Even if you fix "binary_to_term" so that it is safe, I would advise
caution anyway.  While it (term_to_binary+binary_to_term) is a
convenient and easy way to define a data format, it is just too
powerful.  An attacker could provide all kinds of funny data (pids,
refs, funs, very large ints, etc), so even when the binary is validly
formatted, you still need to be very sure that the contents of the
resulting erlang term are fully validated (or are only used in "safe"
ways).  This required validation is very easy to overlook, especially
when the contents of the term are broken down and passed around to
different modules, some of which may not have been written to handle
maliciously formatted data (e.g. consider perl's "taint" mechanism,
meant to help deal with this same problem).

Despite its dangers, I would be interested in what exactly the
problems with binary_to_term are.  So if anyone has the time to look
at it (or already knows), I'd appreciate seeing the results.

/Lon


Reply | Threaded
Open this post in threaded view
|

Security of binary_to_term ?

Björn Gustavsson-3
In reply to this post by Pascal Brisset
Pascal Brisset <pascal.brisset> writes:

> Well this is what security is about, isn't it ? :) Actually I stumbled
> on one of those pathological cases, and I was wondering whether it was
> just a bug or whether additional checks were required anyway.
>
> $ erl
> Erlang (BEAM) emulator version 5.0.2.4 [source]
>
> Eshell V5.0.2.4  (abort with ^G)
> 1> binary_to_term(<<131,111,255,0,0,0>>).
> zsh: 30198 segmentation fault  ./bin/erl

This is bug. There ARE range checks in binary_to_term/1.

I don't know why there is crasch only on certain platform.
It doesn't crasch on Solaris/Sparc, but it crasches on Linux and FreeBSD.

I'll try to look into this problem next week.

/Bjorn
--
Bj?rn Gustavsson            Ericsson Utvecklings AB
bjorn      ?T2/UAB/F/P
                            BOX 1505
+46 8 727 56 87    125 25 ?lvsj?


Reply | Threaded
Open this post in threaded view
|

Security of binary_to_term ?

Arndt Jonasson
In reply to this post by Pascal Brisset
In article <15161.49225.526286.218186>,
Pascal Brisset <pascal.brisset> wrote:
>erlang:binary_to_term/1 generally exits with 'badarg' when applied to
>invalid inputs. Is this behaviour guaranteed ? In other words, is it
>safe to decode untrusted data with binary_to_term ?
>
>The purpose is to send data between untrusted nodes with
>term_to_binary and binary_to_term over TCP, rather than with the
>erlang distribution protocol.

Note that 'binary_to_term' silently accepts garbage lying after the
encoded term:

1> binary_to_term(<<131,104,2,97,1,97,2>>).
{1,2}
2> binary_to_term(<<131,104,2,97,1,97,2,47,11>>).
{1,2}

This has been the case since R6B, and is the intended behaviour.
--
Arndt Jonasson
arndt


Reply | Threaded
Open this post in threaded view
|

Security of binary_to_term ?

Tony Rogvall-3
In reply to this post by Lon Willett
Lon Willett wrote:

> This stuff looks like it's down my alley, so I'll add my $0.02.
>
> Pascal Brisset <pascal.brisset> writes:
> >  > An aside note: If you get the data over TCP, why should it be
> >  > invalid. TCP ensures the data is non corrupted.... or maybe you
> >  > are worrying over rogue nodes ???
> >
> > Well this is what security is about, isn't it ? :) Actually I stumbled
> > on one of those pathological cases, and I was wondering whether it was
> > just a bug or whether additional checks were required anyway.
> >
> > $ erl
> > Erlang (BEAM) emulator version 5.0.2.4 [source]
> >
> > Eshell V5.0.2.4  (abort with ^G)
> > 1> binary_to_term(<<131,111,255,0,0,0>>).
> > zsh: 30198 segmentation fault  ./bin/erl
>
> Ugh!  Crashing the emulator is a bad sign.
>

Yes! extremely bad.

>
> I wouldn't worry overmuch about the crash per se, since all an
> attacker could use that for would be a denial-of-service attack (DOS).
> IMO, preventing DOS attacks is probably impossible (although one
> shouldn't make them _too_ easy).

This must be fixed of course (and will, right bj?rn?)

>
>
> What is a concern is that the segfault indicates that there might be a
> buffer overflow problem, and this possibly would allow an attacker to
> execute arbitrary code on your machine.
>
> Even if you fix "binary_to_term" so that it is safe, I would advise
> caution anyway.  While it (term_to_binary+binary_to_term) is a
> convenient and easy way to define a data format, it is just too
> powerful.  An attacker could provide all kinds of funny data (pids,
> refs, funs, very large ints, etc), so even when the binary is validly
> formatted, you still need to be very sure that the contents of the
> resulting erlang term are fully validated (or are only used in "safe"
> ways).  This required validation is very easy to overlook, especially
> when the contents of the term are broken down and passed around to
> different modules, some of which may not have been written to handle
> maliciously formatted data (e.g. consider perl's "taint" mechanism,
> meant to help deal with this same problem).
>
> Despite its dangers, I would be interested in what exactly the
> problems with binary_to_term are.  So if anyone has the time to look
> at it (or already knows), I'd appreciate seeing the results.
>

This bug is kind of fun, the case is covered (in thought at least, but not
in action).
When the binary is converted into a term a function decode_size2 gets
called to calculate
the number of words needed on the heap. The <<131,111,255,0,0,0>> is
interpreted as a big integer number
(the 111) the length (in bytes) should be LEN=(2^24)*255 (a really big
number) but there is none.
The code then check, if when adding LEN to the current pointer the current
pointer exceeds the end of buffer pointer. The answer is NO it has wrapped
around the address space.
The SKIP and CHKSIZE (external.c) macros need to be reworked to cover this
case!

one way could be to store the  initial pointer and test that the resulting
pointer is between
the initial and the end pointer.

something like this:

#define SKIP(sz) do { \
    char* start= *ext; \
    *ext += (sz); \
    if (((*ext) > endp) || ((*ext) < start)) { return *okp = 0; } \
 } while(0)

/Tony