Restricted Subsets of Perl

A

Andy Glew

Q: are there any ways to restrict a bit of Perl code to a restricted
subset, a sandbox, of Perl - apart from taint?

I'm lazy.

I want a config file format for a tool. I want the config file format
to support expressions, so that the user can say fairly arbitrary
things like "concatenate in different places to the existing tree of
patterns", "replace", "form an outer product".

In fact, the most general form is to allow the user to specify an
arbitrary function from string to string.

I could write a mini-language. But I'm lazy.

In the past, I have occasionally just slurped the config file in and
eval'ed it. But that has obvious security issues; and apart from the
security issues it can be fragile, e.g. if the user code being eval'ed
has name collisions with the rest of my Perl code.

So, what I ask is, are there any restricted subsets of Perl?

---+ Flavoursof Restricted Subsets

Such restricted subsets might vary from

---++ Secure Sandboxes

Like Java, where stuff like filesystem access, etc., is disabled, so
that the code being eval'ed cannot access it. Ditto removal of the
ability to load packages, access to environment variables, etc.

---++ Insulation from calling context

Maybe not secure, but at least preventing access to any variables that
are in the calling environment that are not part of standard Perl.

---++ Read-only insulation

Forgetting the security concerns, I think that I can see a way that
the eval'ed code could be made less fragile. E.g. define the config
file to be an expression that returns a hash array, or the like.

Then:

a) fork
b) the child process has read-only (or, copy-on-write) access
to all state from the parent, but cannot modify any of the
parents' state [*]
c) let the child eval the config file, and generate the
value to be returned
d) dump the return value to a pipe, and read it
in in the parent.

[*] at least, the child cannot accidentally modify the parent.
If really malicious, it might open a debugger on the parent
- but that's beyond what I care about in this insecure version
(and would be handled by the secure sandbox).


---+ Creating the Restricted Context

I can imagine that you could use reflection to walk over the
namespace. If there were a standard way to query - e.g. to ask
"what does this Perl core function do?" (access filesystem, etc.)
then the introspecting code might modify the namespace
to prevent access.


---+ Why am I asking instead of rolling my own?

Because I'm lazy. If it already exists, I will use it.

Perfunctory googling doesn't seem to find any package like
this, but I may not be using the right terminology.

It has been my experience that, if I can describe how to
something, Perl already has done it.

---++ Isn't taint what I want?

I don't think so. But am willing to be educated.

E.g. eval'ing in a taint context may make you moderately more secure,
but doesn't really address the issues of fragility.
 
A

Andy Glew

Andy Glew said:
Q: are there any ways to restrict a bit of Perl code to a restricted
subset, a sandbox, of Perl - apart from taint?

Perfunctory googling doesn't seem to find any package like
this, but I may not be using the right terminology.

Embarassed. Slightly less perfunctory googling led me to the Safe
package. It's what I need.

However, my pondering how to implement this via reflection may still
be worth discussing. If there were a standard query format, allowing
us to enumerate all language elements and identifying what they do,
it might allow more flexible Safe'ing.
 
B

Brian McCauley

A. Sinan Unur said:
CPAN is your friend:

Safe - Compile and execute code in restricted compartments

http://search.cpan.org/~nwclark/perl-5.8.6/ext/Opcode/Safe.pm

Be aware - Safe is flawed. The best known way to escape a Safe
compartment involves returning blessed values. This can easily be
avoided by disabling the bless opcode. Similarly it's probably a good
idea to disable the tie opcode. However there are probably other less
well known exploits.
 
A

Anno Siegel

Andy Glew said:
Embarassed. Slightly less perfunctory googling led me to the Safe
package. It's what I need.

However, my pondering how to implement this via reflection may still
be worth discussing. If there were a standard query format, allowing
us to enumerate all language elements and identifying what they do,
it might allow more flexible Safe'ing.

Alternatively, one could consider a Parse::RecDescent grammar for an
appropriate subset of Perl. It would only have to recognize the
subset, interpretation would still be done by eval(), so the grammar
could be kept simple, it doesn't have to *do* anything.

The grammar below allows only assignment of arithmetic expressions
to scalars. So it allows "$deg = 2 * $pi / 360;", but rejects
"unlink '/etc/passwd';", as it should. It is incomplete (no unary
"-") and too simple to be practically useful, but it shows that the
approach is feasible.

Anno


my $grammar = << 'EOG';

confex: variable '=' expr ';' # start rule

expr: term add_op expr
expr: term

term: factor mult_op term
term: factor

factor: '(' expr ')'
factor: constant
factor: variable

constant: string
constant: number

add_op: /[+-]/
mult_op: m{[*/]}

string: /".*"/
number: /-?\d+/
variable: /\$[A-Za-z_]\w*/
EOG
 
A

Andy Glew

However, my pondering how to implement this via reflection may still
Alternatively, one could consider a Parse::RecDescent grammar for an
appropriate subset of Perl. It would only have to recognize the
subset, interpretation would still be done by eval(), so the grammar
could be kept simple, it doesn't have to *do* anything.

It's the usual
1. build up, out of known safe components
or
2. limit down, taking a general purpose
facility like Perl and trying to restrict it

1. is safer. Less likely to have security holes.

But 1. violates OAOO - the principle of Once and Only Once. Moreover,
if new, safe, features get added to Perl, 2. automatically includes
them, but 1. doesn't.

2. is more likely to have a security hole, because not all of the
holes in the big ciomplicated system may be known.
 
A

Andy Glew

Be aware - Safe is flawed. The best known way to escape a Safe
compartment involves returning blessed values. This can easily be
avoided by disabling the bless opcode. Similarly it's probably a good
idea to disable the tie opcode. However there are probably other less
well known exploits.

That's what I was worried about.

I'm guessing that the blessing and tie'ing use paths which are
relative to the Safe compartment, but which get exported, and
interpreted relative to the true Root.

Unfortunately, the design I came up with for an extensible config file
involved allowing the user inside the Safe to do objects...

Forking the Safe code into its own thread probably makes it safe
against exploits that involve modifying stuff in the parent thread.
Probably have to dump the return values, and strip out anything except
vanilla ints, strings, arrays and lists, maybe refs: no structured,
blessed, or tie'd data to be returned.

---

If I understand you correctly, the problem with Safe is not that the
Safe code can reach out, but that the Safe code can return a data
structure that is a Trojan Horse, which when used by the outside world
feeds stuff back into the compartment.

I.e. the problem that I understand seems to be that you have to
validate very carefully the values returned from Safe. I can even,
possibly, imagine that there is no way to do such validation,
if everything is tied. I don't know how much can be tied.

But if you ignore the return value, are you safe? Or can you tie
something to the destruction / garbage collection of the return value?

Ignoring the return value wouldn't be terribly useful. Can you query
the contents of the Safe, without executing tied code or the like?

Or is it a more general problem? the code inside the Safe can get
access to the outside world even without fooling the outside world to
execute tied code or the like? That would be just a bug in Safe.
 
A

Andy Glew

There are only security issues if the config file is supplied by other,
untrusted, people. Are you running this as root using user config files?
If it's just a regular tool, there are no security issues - nothing
can be gained by 'eval'ling some code the user couldn't do in a much
simpler way.

I'm not sure yet of the trust model. It's just generally good software
to design to not require trust.
At the moment, there are three parties involved in the trust
model:
a) the user
b) the guy who coded up my tool
c) currently, I can pick up a config file from
. and a few other places. that may not be a good idea,
but is convenient, just like putting . in
your path, or using any relative path
in your Perl library path

Anyway, I'm not sure of the trust model. Not damning.




I am more sure of the fragility issues. When I've evaled user
provided strings in the past, I have had bugs when the user
accidentally provided code that collided with names in my Perl code.
I'd certainly like to avoid that.


Therefore, Safe may be good enough for me as it stands now, with
regards to reducing the chance of such accidental collisions.
I may not care enough about security in this situation to
worry about Safe's security holes.
 
B

Brian McCauley

Andy said:
That's what I was worried about.

I'm guessing that the blessing and tie'ing use paths which are
relative to the Safe compartment, but which get exported, and
interpreted relative to the true Root.

Actually that's not the case (I made the same mistake myself when I
first started looking for weaknesses in Safe).

But there is still a security hole because if the code within the safe
compartment returns a value that is blessed into a package with a
DESTROY then that destructor will be called with the true symbol table
root accessible.
Unfortunately, the design I came up with for an extensible config file
involved allowing the user inside the Safe to do objects...

To be really safe you should have a SUID helper and a chroot() :)
If I understand you correctly, the problem with Safe is not that the
Safe code can reach out, but that the Safe code can return a data
structure that is a Trojan Horse, which when used by the outside world
feeds stuff back into the compartment.

That is the nature of the exploits I'm aware of. They can be avoided by
simply disabling the bless opcode (and I'd do the tie opcode too, even
though I don't know how to exploit it).

I am also aware that a lot of people say there are other types exploits
that are possible with Safe.
I.e. the problem that I understand seems to be that you have to
validate very carefully the values returned from Safe. I can even,
possibly, imagine that there is no way to do such validation,
if everything is tied. I don't know how much can be tied.

But if you ignore the return value, are you safe?

No. It still gets destroyed outside the safe compartment. The same may
apply to blessed $@.
Ignoring the return value wouldn't be terribly useful. Can you query
the contents of the Safe, without executing tied code or the like?

Yes. You can wrap the user-supplied code with more code that is
executed inside the safe compartment simply stringifies the result.
Or is it a more general problem? the code inside the Safe can get
access to the outside world even without fooling the outside world to
execute tied code or the like? That would be just a bug in Safe.

As I said, there are respected people who say there are such bugs in Safe.

For a look at how I've tried to work-round some of the more obvious
problems with Safe, take a look at String::Interpolate.
 
A

Andy Glew

Brian McCauley said:
But there is still a security hole because if the code within the safe
compartment returns a value that is blessed into a package with a
DESTROY then that destructor will be called with the true symbol table
root accessible.

I must conclude that this is a bug in Safe. The destructor should be
compiled in such a manner that it is placed back into the Safe
compartment before being run.

And I think this points to the real problem with Safe. So far as I can
tell, Safe seems to affect the execution of the code, not the
compilation of the code. During execution inside the Safe, various
pseudo-opcodes are disabled, and the namespace is restricted. But
code compiled inside the Safe which is executed outside may not be
properly restricted.

Q: is it possible to identify all code compiled inside the safe?
Surely so.

Simply disabling the generation of certain opcodes by compilation is
not good enough. It's the namespace.
No. It still gets destroyed outside the safe compartment. The same
may apply to blessed $@.


Yes. You can wrap the user-supplied code with more code that is
executed inside the safe compartment simply stringifies the result.

For a look at how I've tried to work-round some of the more obvious
problems with Safe, take a look at String::Interpolate.
Thanks.



As I said, there are respected people who say there are such bugs in Safe.

Where are those discussed.... nah, I don't need to know. For my present
usage model , I'm not really that worried about security. I'm more
interested in fragility, reduction thereof.
 
B

Brian McCauley

Andy said:
I must conclude that this is a bug in Safe. The destructor should be
compiled in such a manner that it is placed back into the Safe
compartment before being run.

It is important that support for Safe does not put too much of an
overhead on the rest of Perl. However, IIRC, this is the way it will
work in Perl6 where Safe is not so much of an afterthought.
And I think this points to the real problem with Safe. So far as I can
tell, Safe seems to affect the execution of the code, not the
compilation of the code.

Actually it does both.
During execution inside the Safe, various
pseudo-opcodes are disabled, and the namespace is restricted.

No, the opcodes are disabled at compilation stage not runtime.
Subroutines that are compiled put into the compartment's namespace from
outside can use any opcodes. When such a subroutine is called from
within the comparment it still sees the false namespace root and cannot
compile restricted opcodes with eval() or require() - that's why we need
Safe::Hole. (

Note: Until fairly recently Safe::Hole did not re-instate *INC and the
opcode mask so code called via a Safe::Hole callback couldn't require()
additional modules. This was not good enough for something I was doing
with Safe so I fixed it.
But
code compiled inside the Safe which is executed outside may not be
properly restricted.

It will be restricted in terms of opcodes but not in terms of symbol
tables. Of course if it was compiled with an opcode mask that permitted
eval() then it can do further (unrestricted) compilation at runtime.
Q: is it possible to identify all code compiled inside the safe?
Surely so.

Not that I know of. It would however be possible to detect upon exit
from a safe compartment (either by a return or through a Safe::Hole
callback) all the uppercase named subroutines and overload handlers that
exist within package prefix of the Safe compartment and wrap then using
a varient of Safe::Hole if they were not already so wrapped.

I considered the scale of this task last time I was looking at
Safe::Hole but decided that for my purposes it was simpler to simply
disable bless, tie, overload. If you need code inside the comartment to
create objects it can do so in a controlled manner by calling
subroutines that have been compiled outside and passed in.
Simply disabling the generation of certain opcodes by compilation is
not good enough. It's the namespace.

Er, yes that's my point above (and the opposite of what you said above).
Where are those discussed...

In private, I think.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top