If you ask for STANDARD_RIGHTS_REQUIRED, you may as well ask for the moon


One of the predefined security access masks is STANDARD_RIGHTS_REQUIRED. You see it used in defining the _ALL_ACCESS masks for various objects. Here are just a few examples:

#define PROCESS_ALL_ACCESS        (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | \
                                   0xFFF)
#define EVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3)
#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)

The STANDARD_RIGHTS_REQUIRED mask is meant to be used when defining access masks for object types. I'm guessing it's called STANDARD_RIGHTS_REQUIRED because it's the set of access masks that all securable objects must support. Look at the documentation or just at the definition:

#define DELETE                           (0x00010000L)
#define READ_CONTROL                     (0x00020000L)
#define WRITE_DAC                        (0x00040000L)
#define WRITE_OWNER                      (0x00080000L)

#define STANDARD_RIGHTS_REQUIRED         (0x000F0000L)

Notice that STANDARD_RIGHTS_REQUIRED is just an abbreviation for the union of the four access bits DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER.

Now that you see what it's for, you can also see what it's not for: You're not expected to pass it as the mask of requested access bits when you attempt to open an object. In other words, the following is wrong:

// wrong!
HANDLE hProcess =
    OpenProcess(dwProcessId, FALSE,
                STANDARD_RIGHTS_REQUIRED | PROCESS_QUERY_INFORMATION);

The person writing this code probably thought, "Well, I just want to be able to query information, so I need to pass PROCESS_QUERY_INFORMATION. There's this other thing here called STANDARD_RIGHTS_REQUIRED; since it's required, I'll pass that too."

The "required"ness of STANDARD_RIGHTS_REQUIRED doesn't apply to you, the program opening the object. It applies to the person who is designing the object.

Your attempt to be a "good security citizen" and ask only for the access you need (namely, PROCESS_QUERY_INFORMATION) has backfired due to the addition of STANDARD_RIGHTS_REQUIRED. If you ask for STANDARD_RIGHTS_REQUIRED, you are asking for everything.

Why is that? Notice that STANDARD_RIGHTS_REQUIRED includes WRITE_DAC. If you have WRITE_DAC permission, that means that you have permission to change the security descriptor on the object, at which point you totally 0wnz0r it. You want PROCESS_VM_WRITE access but the security descriptor doesn't let you? No problem. Just set a new security descriptor that grants you PROCESS_ALL_ACCESS to the process object. Tada! You now have all the access in the world.

Moral of the story: Don't ask for STANDARD_RIGHTS_REQUIRED, because only somebody with full control will be able to get it. Ask for what you actually want.

Comments (13)
  1. Matt Green says:

    While I understand that users of the API would think they need to pass that in because of the name, a little bit of critical thinking reveals otherwise. If it was really required to be passed in, omitting it would cause the function to return and set the invalid parameter error code.

    Keeping a tiny little solution off to the side for these sorts of ‘experiments’ is invaluable.

  2. random poster says:

    You know you’ve been reading http://www.crazyontap.com for too long when A) you read the thread title as "…you may as well ask for the moron"; and B) you substitute in the obligatory 18 in your head.

  3. Medinoc says:

    One of the culprits is the wording of the documentation. This extract comes from the DuplicateHandle() page:

    In addition to STANDARD_RIGHTS_REQUIRED, the following access rights can be specified in the dwDesiredAccess parameter for the different object types:

  4. M says:

    "One of the culprits is the wording of the documentation."

    Exactly. I used to always take that to mean, "STANDARD_RIGHTS_REQUIRED must be combined with whatever other access rights you specify."

    Good to know I wasn’t the only one. :)

  5. Niels says:

    #define STANDARD_RIGHTS_REQUIRED         (0x000F0000L)

    I like how the constant pretty much spells out "FOOL" :)

  6. Yuhong Bao says:

    I like how the constant pretty much spells out "FOOL" :)

    Funny isn’t it? The L, BTW, stands for "long".

    STANDARD_RIGHTS_REQUIRED, BTW, illustrates the disadvantages of designing from the author’s prescriptive of the word "required".

  7. Yuhong Bao says:

    I like how the constant pretty much spells out "FOOL" :)

    Funny isn’t it? The L, BTW, stands for "long".

    STANDARD_RIGHTS_REQUIRED, BTW, illustrates the disadvantages of designing from the author’s prescriptive of the word "required".

  8. anonymous says:

    I’m waiting for the day when they’ll replace STANDARD_RIGHTS_REQUIRED with MAXIXUM_ALLOWED for some of the XXX_ALL_ACCESS constants, which would  instantly fix all LUA problems with the CreateXXX() routines.

  9. SERVICE_ALL_ACCESS says:

    Hmmmmm, whoops… this was a very timely post!

  10. Igor Levicki says:

    That is easily solved with the following define:

    #define GIVE_ME_ALL_POSSIBLE_ACCESS_INSTANTLY_OR_I_WILL_REBOOT_YOU_AND_THIS_TIME_I_MEAN_IT 0xFFFFFFFFL

  11. dcook says:

    Well, STANDARD_RIGHTS_REQUIRED isn’t really MAXIMUM_ALLOWED. Basically, it is the set of all permissions that an object must understand. Some objects understand other permissions in addition to the ones specified in STANDARD_RIGHTS_REQUIRED. For example, a file could have a permission bit for setting attributes, which doesn’t make sense for registry keys, and the registry could have a permission bit for enumerating values, which doesn’t make sense for a file. The mapping between the standard rights and the object-specific rights always gives me a headache.

    As far as asking for the moon, it’s always good to cite prior implementations. http://www.putty.nl/wishlist/moon-on-stick.html

  12. CPM says:

    That putty link is a giggle!

Comments are closed.