Alan’s stupid powershell tricks #1

I mentioned last week that I am becoming a big fan of powershell, and that I think it is making me more productive. So, when powershell’s architect replied to my post with a request for examples, I figured the least I could do was post once in a while (I would say weekly, but I’m horrible at keeping schedules). The following is the first post in what I hope to be a long series.

Like most sane people (and unlike 95% of other Windows users), I don’t run Windows with local administrator privileges. (Don’t start with the “I’d do that, but none of my programs would run” argument, because that argument is completely unfounded – I can run for days without needing admin credentials).

Anyway, the above comments aside, I do need to perform “administrative” tasks from time to time (play with device manager, defrag, release my ip address, etc.). With cmd.exe, when I wanted to change system settings, I did this:

C:\>runas

<I pause here for a few seconds trying to remember the order

of the parameters, then…>

C:\>runas /?

RUNAS USAGE:

<snip>

C:\>runas /user:%COMPUTERNAME%\Administrator sysdm.cpl

Enter the password for ALANPA1\Administrator:

Attempting to start sysdm.cpl as user "ALANPA1\Administrator" ...

RUNAS ERROR: Unable to run - sysdm.cpl

1326: Logon failure: unknown user name or bad password.

 

C:\>runas /user:%COMPUTERNAME%\Administrator sysdm.cpl

Enter the password for ALANPA1\Administrator:

Attempting to start sysdm.cpl as user "ALANPA1\Administrator" ...

RUNAS ERROR: Unable to run - sysdm.cpl

193: sysdm.cpl is not a valid Win32 application.

 

C:\>runas /user:%COMPUTERNAME%\Administrator "control.exe sysdm.cpl"

Enter the password for ALANPA1\Administrator:

Attempting to start control.exe sysdm.cpl as user "ALANPA1\Administrator" ...

<finally success>

 

In the first attempt, I mistyped the password. In the second, I was reminded that runas can’t launch sysdm.cpl. Finally, on the third try I get what I want. I could, of course, had created doskey macros to cut out the command line confusion, but that wouldn’t have taken care of the problems of entering the password everytime, and knowing that I needed control.exe to launch control panel applets.

Here’s how I do the same thing in powershell. My profile.ps1 file has the following lines:

#get and store local admin credentials

$computer = (get-content env:COMPUTERNAME)

$gcred = get-credential ($c + "\" + "Administrator")

 

When I launch powershell, I’m presented with a typical windows credential dialog prefilled with my %machinename%\administrator as the user name. I type in my administrator password, press OK, and let powershell load.

Yes – I know I just saved my password in a global variable, and have potentially opened myself up for a vulnerability if someone were to somehow get remote access to my shell. My defense is that I think I’m still 99% more secure than people running with full admin credentials, and I’ll take the risk (for now) to save a little time. I reserve the right to change my opinion on this matter.

The other part of this equation is a little function I wrote called asadmin. It looks like this:

function asadmin([string]$funcname, [string]$arguments)

{

[System.Diagnostics.Process]::Start($funcname, $arguments, "Administrator", $gcred.Password, $computer)

}

 

There are a few things worth mentioning about this function. The first is that I assumed the function would (or could) take “%computername%\Administrator” as the user name, but it wants just plain Administrator for the user name, and the computer name as domain name (the final parameter). Docs aren’t super clear, but it wasn’t hard to figure out when my first attempt didn’t work.

This solved most of my problems, running asadmin progname launched a program with admin credentials. For example, if I need to install an activex control, I can run asadmin iexplore.exe, and an elevated session of IE will launch where I can install the spyware control needed for a particular site. This didn’t solve the problem of launching something like sysdm.cpl without specifying that it launches with control.exe (note that cmd.exe didn’t do this either, but cmd.exe has the start command that basically does a ShellExecute. Long story short was that I ended up just writing a standalone version of start (mine is called se.exe). Since permissions are hereditary, my asadmin function launches se.exe with admin privileges, and uses ShellExecute to launch whatever the command line was. My asadmin function now looks like this:

function asadmin([string]$funcname, [string]$arguments)

{

#turn appname and args into the arguments to se.exe

$local:localArgs = ($funcname + " " + $arguments)

[System.Diagnostics.Process]::Start("se.exe", $localArgs, "Administrator", $gcred.Password, $computer)

 

}

 

Now I can run asadmin sysdm.cpl and change computer settings. One of these days I’ll see if there’s a way to pinvoke from powershell and implement the functionality within a function or cmdlet.

I want to reiterate the fact that I feel weird keeping my password in a global variable, as I don’t know for sure if other processes have any method to access data from my powershell window. Initially, I thought that the password would be stored in a super secret hard to access format, but I poked around a bit to be safe.

First, I wanted to see what methods were available for a SecureString:

UserName Password

-------- --------

ALANPA1\Administrator System.Security.SecureString

 

 

[D:\]

>$gcred |get-member

TypeName: System.Management.Automation.PSCredential

Name MemberType Definition

----            ----------          ----------

Equals Method System.Boolean Equals(Object obj)

get_Password Method System.Security.SecureString get_Password()

get_UserName Method System.String get_UserName()

GetHashCode Method System.Int32 GetHashCode()

GetNetworkCredential Method System.Net.NetworkCredential GetNetworkCredential()

GetType Method System.Type GetType()

ToString Method System.String ToString()

Password Property System.Security.SecureString Password {get;}

UserName Property System.String UserName {get;}

 

My first thought was “uh-oh” – I wonder what get_password does?

[D:\]

>$gcred.get_Password()

System.Security.SecureString

 

Safe so far – wonder what GetNetworkCredential does?

[D:\]

>$gcred.GetNetworkCredential()

UserName Password Domain

--------       --------       ------

Administrator I’mNotTelling! ALANPA1

 

…and there it is in plaintext (changed for my own protection).

I’ll have to play around with it a bit to see how much of a risk it is to have the convenience of having to type my password in when I want to run as admin. I have a feeling I may change the asadmin function to ask for credentials every time, but for now I’ll leave it and see what I can find.