An introduction to MUSH security

MUSHCode for An introduction to MUSH security



An introduction to MUSH security
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This is targetted at administrators, but non-Gods/ Wizards/
Helpers/ Royalty should realize that they are administrators of
their own object and their own commands, and that while security
breaches of Wizard-empowered objects can be disasterous for a
MUSH, security holes in player objects can be equally devastating
for the player. Thus, all players who code or build should read
this.


The first thing any administrator should do is recognize that
everything is a potential security hole. It is not possible to
construct a totally secure system that is at all usable. But
there are steps you should take to increase the security of a
system to reasonable levels.


For MUSH security, the worst possible hole is the God
character. God should own *nothing*, should have no unnecessary
attributes on him, no unnecessary flags (especially not INHERIT)
and should be uselocked (@lock/use me==me). If an object requires
God priviledges to run it is most likely either a result of a bad
configuration of the MUSH, or an object which can muck with
wizards; the first can be solved (a typical example is the
default permissions of the @attribute command, which should be
made wizard-runnable) and the second is probably not necessary.


The God character is a special case, and easily handled. The most
common potential holes are wizard-empowered objects, those that
control every other object on the MUSH. To get an idea of how
many of these exist, type:

@create TestObject
@set TestObject=INHERIT
@search eval=controls(##,TestObject)

Every listed object has the potential to destroy the entire MUSH
in a few seconds.


How an object becomes wiz-powered:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

An object has wizard powers if:

It is set WIZARD (by the God character)
It is owned by someone set WIZARD and is set INHERIT
It is owned by someone set WIZARD *who* is set INHERIT

Note carefully the third option. Any wizard who is set INHERIT
makes everything they own wizard-powered and thus a potential
security hole of gargantual proportions.

==== WIZARDS SHOULD NEVER BE SET INHERIT ====

Wizards are humans, and prone to make mistakes; this includes
creating insecure objects. If a wizard is set INHERIT, any
insecure object they create is wizard-powered. If they are not
set INHERIT, they have to explicitly grant wizard powers to the
objects they create one by one. Thus, wizards should never be set
INHERIT. If a wizard is set INHERIT, they should remove the flag
immediately. If they complain about being too lazy, ask them if
they are willing to reconstruct the entire MUSH from scratch if
it should be damaged due to a potential security hole in their
objects.


Next and fairly obviously, only objects which need to perform
wizard-power operations should be set INHERIT. If something
doesn't need to perform wizard operations (and if you have
creative players, you know how much you can get away with sans
wizbit) remove the INHERIT flag.

I estimate that if the average MUSH follows the steps outlined in
this section, the number of wizard-powered objects will be cut in
half.


Security holes: Locks
~~~~~~~~~~~~~~~~~~~~~

(This applies to objects who's use you want to restrict;
obviously, public globals won't be uselocked in this manner)

@lock/use object=me

Only I can use the object now, right?

==== WRONG ====

Ignoring @averb attributes for now, this is one of the most
common security holes seen on MUSHes. The syntax of that lock
sets what is known as an is-or-carry lock.

@lock/use Foo = Fizbin

This means:

A can use Foo if A *is* Fizbin

AND

A can use Foo if A *carries* Fizbin

(that is, Fizbin is in A's contents list)

Consider a wizard that does:

@create WizToys
@set WizToys=INHERIT
@lock/use WizToys=me
&FAB_CMD WizToys=$fab:@do some wonderful stuff

If a player can convince the wizard to drop WizToys in a room
that the player owns, the room can run the fab command, since the
room passes the "carries" part of the lock.


==== CORRECT SYNTAX ====

@lock/use object = =me

The "=me" is an *is* lock ONLY. This is the secure way to lock
objects.

Now do:

@dolist search(me eval=controls(##,TestObject))=
@pemit me=[name(##)](##) - [lock(##/use)]

This will show you the UseLock of every wiz-powered object you own.
You should make sure that all of your own objects have the
correct lock usage. Then run do:

@dolist search(eval=controls(##,TestObject))=
@pemit me=[name(owner(##))]: [name(##)](##) - [lock(##/use)]

This checks everyone's wiz objects. If you find insecurely
locked ones, send mail to the wizard and let them know, or fix
the lock yourself.


Security holes: Commands
~~~~~~~~~~~~~~~~~~~~~~~~

Now that you've made sure that only those who need access to
commands can run them, what about commands that everyone should
be able to run, like globals?

Here's the nastiest command possible:

&SOME_CMD Global object=$+do *:@fo me={%0}

This lets anyone run any command, fairly obviously, and you're
not going to find this on many MUSHes (at least, I hope not).

But here's a similar example that's equally nasty:

&SOME_CMD2 Global object=$+do2 *:@fo me={@pemit %#=You +do2'd: %0}

It looks harmless enough at first, but

==== @force evaluates its 2nd argument ====
==== before enqueuing the results ====

What's that mean in English? If I do:

@force me={@pemit me=test: %va}

And my @va attribute is:

haha! ; @destroy me

Then the 2nd argument of @force becomes, on substitution:

@pemit me=test: haha! ; @destroy me

And *both* of these commands are run. Adios, amigo.

==== AVOID @force IN GLOBALS LIKE THE PLAGUE ====

At the very least, be extremely careful. Test it with arguments
with semicolons. One way to secure an @force is:

@force me={@pemit me=test: {%va}}

This should work properly. The {} around the %va delay its
evaluation, so that the 2nd argument of the @force evaluates to:

@pemit %#=You +do3'd: {haha!; @destroy me}

This runs just fine.


Security holes: Attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~

Players should beware of this one especially; on Narnia, I had a
public bulletin board (non-wiz, non-global) which did pretty much
what I'm about to say is a big mistake. It patched it before
anyone exploited the hole, however.


A potential mail editing system might look like this:

$-*:@switch member(v(EDITING),%#)=
0,{@pemit %#=You're not editing a message!},
{
&MESSAGE-%# me=[v(MESSAGE-%#)]%r%0;
@pemit %#=(message continued)
}

Danger! Danger Will Robinson! If a user (#1234) types:

-$breakin *:@force me={%0}

Then MESSAGE-#1234 on the object becomes:

$breakin *:@force me={%0}

This should look familiar. Ways to avoid this:

- Never set arbitrary attributes (user input) on
objects which are publicly accessable. For example,
store the actual text strings comprising a bulletin
board on a data object, not the global object.

- Set any attribute thus created NO_COMMAND:

@set obj/attrib=NO_COMMAND

- Never allow a prefix to start with user input, e.g.

&BBPOST bboard=$post *:
@vn me=add(%vn,1);
&POST-%vn me=%n -- %r %0;

Setting attributes with arbitrary names is also a major no-no,
but that's less common.


Security holes: @averbs
~~~~~~~~~~~~~~~~~~~~~~~

Anyone can potentially run these, so watch out. If you want to
restrict their use, put an explicit check into the code.


--
Joshua, aka Algol@NarniaGolden