From: behrends@dfki.uni-kl.de (Karl Reimer Behrends)
Subject: Operation Tin Opener (Was: A mudlib for MudOS)
Organization: DFKI Kaiserslautern
Date: Sun, 20 Dec 1992 17:59:17 GMT

In article <92352.104241GREESE@MAINE.MAINE.EDU>, <GREESE@MAINE.MAINE.EDU> writes:
|> The arches of Nightmare announce the primary release of the Nightmare
|> mudlib.  This is a mudlib meant to be used with MudOS 0.9.* drivers.
|> Now, those of you who have been looking for a useable MudOS mudlib need
|> look no further.  The Nightmare mudlib includes the following features
|>
[Stuff deleted]
|> 
|>     security: Great security only MudOS based mudlibs can provide
|> 
Well, while the rest of the mudlib is nice (and I mean REALLY nice) I can't
decide whether the security reminds me more of a sieve or Swiss cheese.
This is, however, _not_ the driver's fault. It's chiefly the basic design
of the mudlib that lets you run into problems. But see for yourself.

1. Root uid to let / for sale
-----------------------------

Let's start with the master object. Since it can do anything, it makes
sense to look for security leaks there - or in any other object with
uid "Root". First we grep for uses of export_uid. Among others, we find:

void save_player_to_file(object player) {
	string name;
	name = (string) player->query_name();
	if (!name) return;
	if (!seteuid(ROOT_UID))
		write("Seteuid failed!\n:);
	export_uid(player);
	player->actually_save_player(name);
	seteuid(name);
	export_uid(player);
	...

Nothing prevents us from passing one of our own objects as an argument
to that routine, thus procuring global access for our object. If we
make query_name() return ROOT_UID, we don't even have to generate an
error in actually_save_player() to prevent changing the uid back.
If somebody were to introduce a check, say, for interactive(player),
we could still call it after a carefully timed for-loop which would
stop execution due to eval cost too big in actually_save_player() and
end up with our player object having uid "Root".
Considering that I repeatedly stated only some time ago how dangerous
export_uid() is and that it needs being checked and double-checked, one
can only wonder at this carelessness.

Another fine example can be found in all the command routines that cope
with files, for example /bin/dev/_cp. It sets it effective uid to the
uid of previous_object(). Thus, if any wizard has a call_other()
somewhere where the function can be chosen freely, his files are
automatically writable by everybody. Luckily the master object (as well
as numerous other objects in /bin) has this possibility. Just consider
the flag() routine in master.c which can be used for this purpose.
The additional valid_write() calls in _cp.c are nt important.
this_player() can be set to be a monster of your choice, and since
valid_write() checks find_living() instead of find_player() there
is no trouble at all. Otherwise you'd have to wait for an arch to
be present to use him as this_player().

2. Backbone uid - the chameleon
-------------------------------

If AUTO_TRUST_BACKBONE is defined as a driver compile time option,
every object from /obj or /std gets the uid of the object that loads
it. This is especially nice since there are uncountably many
possibilities to load or clone a backbone object from one with
uid "Root". A nice example is /std/user/edit.c where you can use
the lines() routine to write to any file you like.
Second, once loaded, such objects provide you with nice examples
for the uid-of-previous-object-will-be-Root approach.

3. Off with his head - applications of exec()
---------------------------------------------

Since MudOS now uses protected simul_efuns instead of valid_exec() to
ensure security and this has been left out in the Nightmare mudlib,
anybody can disconnect an arch and exec() himself onto his linkdead
body, thus gaining all his privileges.

Even if the check of valid_exec() were still present in comm.c, it
only checked for the PROGRAM name that contained the exec() instead
of the name of the OBJECT that called it. Thus inherit /bin/dev/_su
in your object, set up some variables and call the static function
swtch_player() locally. Done.

4. The power to command
-----------------------

One of the nice things about leaving out the second parameter of
command() instead of enforcing its use (from the hacker's point
of view) is that you'll get a plethora of security leaks by being
able to force arches to do things like copying YOUR example of
how /adm/master should look like to /adm. Just have a look at
/std/user/tsh::tsh() which isn't protected at all or at the
combination of set_whimpy_dir() and run_away() in /std/living/combat.
Even making both of them static doesn't help since you can always
change the exits of a room and run_away() can be called by giving
the arch in question a stab in the back. ;-)

5. The danger of using pointers - a non software engineering approach
---------------------------------------------------------------------

Master.c has more in store for us than just being a donator for uid
"Root". Just have a look at query_access() and query_groups() in
/adm/access which gets inherited by the master. They return mappings,
and mappings can be modified. Ever wanted to add yourself to the list
of arches? Or how about giving everybody global write access? Just
modify the desired entries in the mapping returned.

6. The wizard "Root" - an endangered species
--------------------------------------------

Well, it seems that nobody checks for the filenames of objects look
like "/wizards//hacker/cheat.c". They will get "" as their uid. They
can thus write to "/wizards//Root/cheat.c". Once loaded, this file
will have uid "Root" and therefore global access.

				Reimer Behrends

---
Thus forgetting tricks and play
For a moment, lady dear;
We would wish you, if we may,
Merry christmas, glad new year.

-- Lewis Carroll, From a fairy to a child.
Newsgroups: rec.games.mud.lp