u() and you. AKA: How to give out permissions without realizing it.
-
@Sponge said in the Random Bitching thread:
Help me out here. I always u(), I never eval(), I work in TinyMUX. What's the deal?
This is meant as a primer in security on u(), not as a direct answer to Sponge's question, however I touch on a few points that he, and other MUX runners, should note.
In TinyMUSH, u() did it's evaluation based on the TARGET. This tends to form the basis of the functionality of the derivative codebases. This means that u() evaluates from the perspective of the object performing the function. Since most codebases today are lineage-traced to TinyMUSH, this behavior has carried over. It is like this in MUX.
Consider this example. I am an Immortal/Wizard:
&TEST me=[set(*Guest1, Royalty)] @set me/test=VISUAL
Now, anyone can do this:
(As a guest): think [u(*Rook/TEST)]
Lo and behold, Guest1 is now a Wizard. Ta Da! WTFJH, you ask? Well, since u() evaluates as the target, it was using my permissions (as a Rhost Immortal/Wizard) to execute that function. Once you think about how many games upload code given to them, or they find on the internet, the dangers of this behavior can quickly scare the shit out of any non-coding game owner. Imagine if Anomaly's +JOB code had a hidden command in the file that did exactly this? This has actually happened on games in the past, this exact sort of hidden backdoor exploit that was put in place by malicious staffers with WizBits in the event that they got fired.
In Penn and Rhost, but only in these two codebases, you have the option to disable this type of behavior using the safer_ufun config parameter, which forces the game to evaluate attributes on things you do not control at your bit level or the object's level, whichever is LOWER**. Since objects do not have bits, this only makes sense on INHERIT objects... which means the stuff in your Master Room that are usually owned by a Wizard.
@admin safer_ufun=1 Set. think [u(*Rook/TEST)] Permission denied.
Now Penn and Rhost will give you the bold-faced middle finger. MUX... well, you really need to check all of your code for side-effect functions being used.
**And in Rhost, additional security is in place to force the target to have SIDEFX flag set on the target before side effect processing (such as set() ) is allowed. The benefit of this two-fold security is that you can set only specific objects to be allowed to process SIDEFX functions such as set(), things that really should have it because of coded systems.
-
@Rook's first example would fail in TinyMUX. Functions are always processed with the permissions of the item enacting/originally calling it. In the first example, a guest is running it. This is why the children of room parents can't list dark wizards, even if the room parent is Wizard or Inherit to one; the room child doesn't have the privileges.
Things fall apart if the function originates on an object bring called via a command.
$+do it:@set %#=royalty
will happily set the enact or royalty if the object has permissions.In TinyMUX, you can change the enactor of a function by using
objeval()
. I normally use this withlwho()
to get a simple list of anyone the player can see on the who list, excluding dark wizards automatically. -
This was just confirmed in TinyMUX, both in 2.7 and 2.10, @Thenomain.
> @version MUX 2.7.4.34 #7 [2010-NOV-19] Build date: Thu Feb 14 14:45:38 CST 2013 > think %n: [lflags(me)] Tester: CONNECTED KEEPALIVE SAFE STICKY > @set me=immortal Permission denied. > think %n: [lflags(me)] Tester: CONNECTED KEEPALIVE SAFE STICKY > think bittype(*Wizard) 5 > think get(*Wizard/va) [set(*Tester,immortal)] > think u(*Wizard/va) > think %n: [lflags(me)] Tester: CONNECTED IMMORTAL KEEPALIVE SAFE STICKY
Yes, it is a silly example. No Wizard deserves a bit that would have this example setup. But look at the dangers that this presents.
-
-
The ability to globally disable side-effect functions would be super-sweet.
-
very belatedly, I wanted to note that on pennmush the only reason this works at all is the visual flag. Without it the player doesn't have permission to get the attribute. U is first and foremost a get call, so if you don't have permission to grab the attribute it fails. And any wizard setting themselves visual shouldn't be a wizard anyways. Afterwards the only thing stopping it is safer_ufun yes.
Also, it's @config on penn, @config/set safer_ufun=1 or yes.
Case in point on pennmush 2.8.5:
ex me/va VA: [set(*Tester,Royalty)] 'On tester:' th [get(*okra/va)] #-1 NO PERMISSION TO GET ATTRIBUTE ex *okra/va No matching attributes. th [u(*okra/va)] #-1 NO PERMISSION TO GET ATTRIBUTE
Edit: I would assume rhost is the same?
-
@alzie, you're kind of missing the point. The point is the processing of u() and how it can be dangerous, versus, say, get(). Almost all coders I've met pull attributes/data from objects using u() because that is the way they learned or were taught.
The point is that u() evaluates as the target being called, not as the caller. New coders tend to think that u() will execute as them (the caller), with THEIR permissions. It doesn't. u() evaluates from the perspective of the object performing the function.
If you u() on an object/player with higher permissions, you will get back higher permission-ed data. As even Theno says, it is unexpected behavior, the same thing that most coders say when I show them that.
Only Rhost and Penn allow you to STOP that behavior with safer_ufun. MUX is vulnerable, with no fix (at this time).
-
@Rook You're missing the point of my post. I know what you're saying and I agreed with you. What i'm saying is that your example is fundamentally flawed in pennmush because the only reason it works at all is because you set your attribute as visual. A person cannot run U in pennmush on a wizard object as a normal bit because they don't have permission to grab attributes off the object. It automatically fails before passing go because you cannot GET the attribute. As another example:
#Code set on a wizard object &test object=[pemit(*TEster,test)] #Flags of object: Flags: UNFINDABLE WIZARD SAFE #ex object/test No matching attributes. #th [get(object/test)] #-1 NO PERMISSION TO GET ATTRIBUTE #th [u(object/test)] #-1 NO PERMISSION TO GET ATTRIBUTE
-
Royal bits or anyone else with the see_all power can however get the attributes, which leads to the potential of all sorts of shenanigans even when safer_ufun is turned on.
-
@Groth said:
Royal bits or anyone else with the see_all power can however get the attributes, which leads to the potential of all sorts of shenanigans even when safer_ufun is turned on.
If your wizards run around giving random bits see_all and your royals run around running random code set on your wizards, i would say you have bigger problems than code security.
-
Well. It's working as designed. I agree, it's a problematic design for getting random people to write secure code.
I've not had any problems with u() because my default assumption is to use v() or get() (for other objects), and if I really do need to invoke off-object code, then I'm mindful of the execution privileges. For example, in my hooks code for handling pose-breaks-- which really does need to run use-code on user-objects-- I used objeval(). This drops privileges and runs things as the specified user, which makes it even more clear exactly what's going on. Grant you, this is on code already marked INHERIT to a WIZARD code holder.
Other than that, it's a matter of being mindful of where data comes from and how to properly sanitize input. c.f. xkcd:
You're right though, it'd be nice to help people make things safer. Replacing mushcode entirely would be a better direction, I think.
-
@Chime said:
You're right though, it'd be nice to help people make things safer. Replacing mushcode entirely would be a better direction, I think.
There's evennia if you'd like to take that dive.
-
@Alzie said:
@Chime said:
You're right though, it'd be nice to help people make things safer. Replacing mushcode entirely would be a better direction, I think.
There's evennia if you'd like to take that dive.
In the context of arbitrary users writing code, making it a language that interfaces with the host platform scares the hell out of me. Lua would scare me a little less given the existing platforms that have managed to isolate hosted code securely.
I haven't poked at Evennia in any meaningful way. I'd guess they never intended not-administrator users to add python modules.
-
@Sponge said:
@Alzie said:
@Chime said:
You're right though, it'd be nice to help people make things safer. Replacing mushcode entirely would be a better direction, I think.
There's evennia if you'd like to take that dive.
In the context of arbitrary users writing code, making it a language that interfaces with the host platform scares the hell out of me. Lua would scare me a little less given the existing platforms that have managed to isolate hosted code securely.
I haven't poked at Evennia in any meaningful way. I'd guess they never intended not-administrator users to add python modules.
There was a time I was interested in Evennia. That has passed. Not because it's a bad idea, but because of how it was implemented. They moved to a library based system, so essentially it's nothing more than a specialized socket leaving you to write libraries that interface with more libraries to make anything beyond basic commands. This was a bad design decision I believe. One that makes it largely inaccessible to a novice programmer. Or basically, if you have no idea what I just said, don't use Evennia.
-
@Alzie said:
One that makes it largely inaccessible to a novice programmer. Or basically, if you have no idea what I just said, don't use Evennia.
Yup, I was pretty excited to hear about Evennia, and give it a try, but not understanding what anyone was saying seemed to be my big block with it. Getting a hand with mush softcode, strangely, seemed much easier.
-
Not that I would ever code something like that, that would make a person into a wizbit I still want to give a shout out in thanks for this. I checked all the code I ported in and am not vulnerable to it, but at least I will try and break my u() habit, which I usually type just because it's more universal. I'd use v() except I hate having functions and databases on the same objects as actual code. Guess I'm weird like that... so get() here I come. I suppose those two extra characters won't kill me
-
@Lithium said:
Not that I would ever code something like that, that would make a person into a wizbit I still want to give a shout out in thanks for this. I checked all the code I ported in and am not vulnerable to it, but at least I will try and break my u() habit, which I usually type just because it's more universal. I'd use v() except I hate having functions and databases on the same objects as actual code. Guess I'm weird like that... so get() here I come. I suppose those two extra characters won't kill me
To be clear, get and u do completely different things. Sometimes u is necessary, sometimes get is necessary. If you're using U to get an attribute and nothing more then you should be using get/xget. If you actually need to run code stored in an attribute you still want U. As for v, it's a shortcut for get that grabs an attribute relative to the object that it's ran on.
-
@Alzie said:
@Lithium said:
Not that I would ever code something like that, that would make a person into a wizbit I still want to give a shout out in thanks for this. I checked all the code I ported in and am not vulnerable to it, but at least I will try and break my u() habit, which I usually type just because it's more universal. I'd use v() except I hate having functions and databases on the same objects as actual code. Guess I'm weird like that... so get() here I come. I suppose those two extra characters won't kill me
To be clear, get and u do completely different things. Sometimes u is necessary, sometimes get is necessary. If you're using U to get an attribute and nothing more then you should be using get/xget. If you actually need to run code stored in an attribute you still want U. As for v, it's a shortcut for get that grabs an attribute relative to the object that it's ran on.
I had forgotten about needing U to run functions on another object, as you need to evaluate them and not 'get' them when I wrote that bit. A good reminder though Thank you.