Tuesday, June 1, 2010

Episode #97: Make me a Sandwich

Tim

One of the best ways to protect your computer is to run with lower permissions (not root or admin). Lots of security problems can be mitigated with this principle. While running as a regular human, we sometimes need to call upon great power to do some tasks. But remember, with great power, comes great responsibility.

So how do we call upon the super human powers in PowerShell? The command looks like this:

PS C:\> Start-Process "$psHome\powershell.exe" -Verb Runas
-ArgumentList '-command "command to execute"'


For example, I want to start Terminal Services:

PS C:\> Start-Process "$psHome\powershell.exe" -Verb Runas
-ArgumentList '-command "Start-Service TermService"'


Or Stop the service:

PS C:\> Start-Process "$psHome\powershell.exe" -Verb Runas
-ArgumentList '-command "Stop-Service TermService -force"'


The "-Verb Runas" means that the command should be run as the administrator. The Argument List parameter takes the commands to be passed to the elevated instance of PowerShell.

There are a few problems with this. First, depending on the settings in Vista or Windows 7, you will get the UAC popup. Second, this actually creates a new instance of PowerShell with new environment variables and a different working directory. Third, errors are pretty much impossible to read since the session is destroyed upon completion of the command. Forth, the command is pretty long and goofy. Fifth, it isn't so easy to say:

Start-Process "$psHome\powershell.exe" -Verb Runas
-ArgumentList '-command "Make me a Sandwich"'


Here is a simplified function that can be used to call elevated commands. I'd suggest adding it to your profile so it is ready when you need it.

function sudo
{
param( [string]$arguments = $args )
$psi = new-object System.Diagnostics.ProcessStartInfo "$psHome\powershell.exe"
$psi.Arguments = $arguments
$psi.Verb = "runas"
[System.Diagnostics.Process]::Start($psi)
}


Commands can be called like this:

PS C:\> sudo Stop-Service TermService -force


It does have the same limitations as above, execept it is easier to type.

Oh well, in true Windows form, we can run the entire shell as admin via the GUI (meh) or by using Ed's method.

Ed
So, Tim started his section by just saying "Tim". I guess we're into minimalism this week, so I'll just start with "Ed".

You know, this whole topic of elevated command line access comes up in my SANS classes a lot. It doesn't plague students who show up with Windows XP. But, those people who arrive in class with Windows Vista or Windows 7 are sometimes surprised by it. They logon to their laptop GUI as a user in the administrator's group, and then invoke a cmd.exe. Then, they try to run certain commands that alter the operating system configuration, and they get an "Access is denied" message. I'm not talking about UAC here, that delightful little dialog box Windows displays any time you want to do something interesting. I'm talking about not having the privileges to do what you want. Consider this nice little message from my Win 7 box, using the service controller command to try to stop the Windows Search service associated with indexing:
C:\> sc stop wsearch
[SC] OpenService FAILED 5:

Access is denied.
After this occurs in my class, a hand usually goes up, and I get asked a question about why it doesn't work. "You don't have elevated privileges," I respond. "But, I logged in as admin," they often shoot back. "Ahhh, but Microsoft is trying to protect you from yourself. They seem to think that the big, bad, scary command line is just too powerful for someone to use with admin privs unless they explicitly ask for such privs. So, when you logon with an admin account, and launch a cmd.exe, you don't have full admin privs in the resulting command prompt. You need to launch an elevated command prompt."

I then show them how to launch one at the GUI. Simply go to your Windows icon on your tool tray (still called the "Start" menu, but since it doesn't say "Start" anymore, I don't personally call it that). Click on it and do a search for cmd.exe. When you see the icon for cmd.exe pop up, right click on it and select "Run as administrator". Alternatively, you can point your mouse to hover over the cmd.exe and hit CTRL-SHIFT-ENTER to launch it with elevated privileges. I prefer the right-click action myself, because it just feels kinda weird to hover my mouse over something and then hit CTRL-SHIFT-ENTER. When your cmd.exe launches, its title bar will say "Administrator: cmd.exe", giving you a reminder that you have an elevated prompt.

If you find yourself frequently needing an elevated command prompt, you can create a shortcut to cmd.exe and place it on your desktop. Right click on your shortcut, go to Properties, click the "Shortcut" tab, and click "Advanced". Check the "Run as administrator" box. You may want to name your shortcut ElevatedCmd.exe or something to remind you about its use.

Well, this is all well and good, but how do you do launch an elevated command shell at, you know, the command line? Well, for that, we rely on the good old runas command. At a non-elevated prompt, you could simply run:

C:\> runas /u:administrator <command>

When prompted, type in the administrator's password, and you are good to go.

That command can be whatever you'd like, such as the "sc stop wsearch" or "sc start wsearch". Or, you can even launch another, elevated cmd.exe with:

C:\> runas /u:administrator cmd.exe

You know, all this musing about runas and (as Hal is certain to point out) sudo reminds me of a fun conversation I had with fellow InGuardians dude Tom Liston a few years ago. I told him that I was creating a new Windows command called "don't runas". It would take whatever command you specify, and not run it. But, in not running it, it wouldn't just do nothing... it would literally do nothing. It would actually run a bunch of nops, with the privileges of the user you specify. Tom then said we could do a Linux equivalent, called "sudont". Then, in a fit of creativity, we thought about offering a cloud-based Application-as-a-Service version of this, which would allow a user to kick off a dontrunas or sudont on their machine, and it would be submitted via an empty SOAP request to a bunch of servers on the Internet that would do nothing very quickly and in parallel, sending a response back with nothing in it. We decided that we could actually charge for such a service, making big money from users for doing nothing for them. But, then, we realized that we might get sued by various Certificate Authorities for infringing on their business models, so we never really implemented our idea. Which, come to think about it, actually makes sense. We were going to make a dontrunas and sudont command, but we just never got around to doing anything with it.

Hal (just keeping with the theme here, people)

Hey Ed, doesn't the US Congress have the patent on the "doing nothing in parallel" idea? You'd better watch out there. Oh wait, you said "doing nothing quickly and in parallel". I guess you're OK after all.

I'd like to thank my co-authors for serving up another easy one for me this week, as I'm currently in transit between one conference in the next. Of course the command to run a single command with superuser privileges is the venerable sudo command:

$ sudo grep ^hal: /etc/shadow
[sudo] password for hal: <not echoed>
hal:LIKEIMREALLYGOINGTOSHOWYOUMYPASSWORD.YOUMUSTBECRAZY.:14579:0:99999:7:::

sudo prompts the user for their own password. Assuming the system administrator has granted the user sudo access to the command the user is trying to execute, the command will run with elevated privileges.

Of course, those "elevated privileges" need not be root. With the "-u" option, you can specify another user to run your command as:

$ sudo -u mysql ls /var/lib/mysql/mysql
columns_priv.frm help_relation.MYI time_zone_leap_second.frm
[...]
help_keyword.MYI tables_priv.MYD user.frm
help_relation.frm tables_priv.MYI user.MYD
help_relation.MYD time_zone.frm user.MYI

Why wasn't I prompted for my password this time? sudo "remembers" that you typed your password recently and doesn't prompt you again as long as you keep using sudo within a relatively small interval. The default is 5 minutes, but you can customize this in the /etc/sudoers configuration file.

Anyway, I normally try to force my DBAs to use sudo instead of su-ing directly to the user mysql, oracle, etc. Of course they get tired of having to type "-u mysql" on every command. Just FYI, you can put the following in your /etc/sudoers file so that all members of the Unix group "dba" will sudo to the "mysql" user by default:

Defaults:%dba       runas_default = mysql

Of course, those users will now have to explicitly "sudo -u root ..." to do anything as root.

By the way, as Ed mentioned in his section, environment variables getting reset can be a problem when you're using a tool like sudo or runas to do things as an alternate user. Your DBAs are going to have particular problems with this, since most of their scripts are going to assume that they're logged in directly as the "oracle" user or whatever and have all of the environment variable settings that go along with logging into that account. You may want to look into the env_keep option in your /etc/sudoers file to selectively preserve certain environment variable settings your DBAs are expecting to have.

Of course, your DBAs are immediately going to try to "sudo -u oracle /bin/bash" or "sudo -u oracle su" in order to get an interactive shell. At this point they've "escaped from sudo" and you're no longer getting an audit trail of what they're doing. You can try to prevent them from doing this by writing your /etc/sudoers config in such a way as to not allow them to execute these commands, but remember that many Unix commands allow "shell escapes" to an interactive shell:

$ sudo vi
(inside of vi) :shell
# id
uid=0(root) gid=0(root) groups=0(root)...

There is an /etc/sudoers option called "noexec" that you can turn on to disable shell escapes from programs (which it does by some really clever substitution in LD_LIBRARY_PATH). "noexec" is useful although it can break programs like "crontab -e" that rely on being able to exec() your editor to let you edit your crontab.

There's also the "sudoedit" option for allowing people to securely edit privileged files. sudoedit uses superuser privileges to make a copy of the file that is writable by the user, edits the file as the user, and then uses superuser privileges to put the edited file back into place.

One last item bears mentioning as long as we're talking about sudo. Output redirection can be a problem with sudo:

$ cd /etc
$ sudo awk -F: '($2 == "") { print }' /etc/shadow >empty_passwds
bash: empty_passwds: Permission denied

The problem here is that while the awk command happens with superuser privileges, the output redirection to the file empty_passwds happens in a subshell that is not running via sudo. Since your normal user account doesn't have write permissions under /etc, you get the "Permission denied" message.

The work-around is to use "sudo tee" in a pipeline:

$ sudo awk -F: '($2 == "") { print }' /etc/shadow | sudo tee empty_passwds >/dev/null

The tee command writes its input to a file and also to the standard output. In this case, we just care about creating the empty_passwds file, so I redirect the standard output to /dev/null to discard it.

Whew! For an "easy" Episode, I sure ended up packing a lot in here. I hope this helps you with your sudo-ing in the future.