Pentest Deep-Dive: Custom RUNAS

Information Security often exists in a delicate balance with business demands. Organizations weighing security against functionality, cost, ease-of-use, or time for development, commonly choose imperfect but realistic compromises.

For the most part, these compromises allow for progress towards business objectives while maintaining an acceptable balance. Other times—especially in the absence of a proper security evaluation—organizations can inadvertently deploy solutions that drastically increase their risk.

During a recent internal network penetration test we came across a prime example of an unbalanced solution. To solve the old problem of "How do we allow our users to install approved applications on their systems?" this organization developed a custom solution in-house. Installation scripts for the approved applications were placed within a directory on the C:\ drive and, via an easy-to-use GUI, users could select which program to install, triggering the associated script.

As a security consideration within this environment, users were not administrators on their assigned systems. However, most of the approved applications require administrator privileges to install.

As a security inconsideration, all the user workstations have been configured with a shared local administrator password so a single version of the script could be deployed on every system.

At some point, we imagine the idea was floated to use the RUNAS command in a batch file to execute the installation as the local administrator. However, RUNAS expressly does not accept including a password on the command line as doing so would inevitably lead to weak deployments. As described by Microsoft’s Raymond Chen:

This was a conscious decision. If it were possible to pass the password on the command line, people would start embedding passwords into batch files and logon scripts, which is laughably insecure.
— https://blogs.msdn.microsoft.com/oldnewthing/20041129-00/?p=37183

Raymond kindly offers an option for those looking to head down this bad decision rabbit-hole in the form of creating a custom executable using the “CreateProcessWithLogonW function, which does allow for plaintext passwords from the command line. And as such, a custom executable which solved this corporate need was born. For the context of this writeup we will call this RUNAS_A.exe.

At first glance we can see the developer attempted to avoid using plaintext passwords as command line arguments by instead requiring an encrypted value. An example of this:

     RUNAS_A /user administrator /pass <EncryptedPassword> “C:\installApp\install.bat”

But wait, couldn’t any user run any command as the local administrator by merely using the same command line string with a different command? e.g.:

     RUNAS_A /user administrator /pass <EncryptedPassword> “C:\evilApp.exe”

You guessed it. A static value whether “encrypted” or not does not prevent abuse given the design of this application. Given this knowledge, an attacker could perform horizontal and vertical (per system) privilege escalation to all machines sharing the Local Administrator credential.


Lets dive a little deeper…

When first looking at the RUNAS_A command string, we noticed the password value was base64 encoded. For example:

     RUNAS_A /user admin /pass lkB6RJYwDDFtbxckaGeaUuQwWnXpcAsuHEmaMNAhrQ== “C:\installApp\install.bat”

Our hopes that someone had made the classic mistake of confusing encoding with encryption were soon dashed as the decoded password string had likely been encoded to avoid either unprintable or control characters.

Decoded Encrypted Password

Decoded Encrypted Password

The password was easily decoded but still encrypted. With access to the RUNAS_A executable, we took a look to see what it was doing regarding encryption. Before starting up any serious reversing effort, we tried running the application with no options provided.

Running RUNAS_A.exe

Running RUNAS_A.exe

One of the options of RUNAS_A is an "encryption mode" to let you encrypt passwords before use. Let's try a quick known-plaintext attack to see if we can figure out what is going on.

Encrypting a Password with RUNAS_A.exe

Encrypting a Password with RUNAS_A.exe

Decoding our base64 output gives us the encrypted password.

Decoded Encrypted “testpassword” Password

Decoded Encrypted “testpassword” Password

Time to take a look at the output and identify the encryption algorithm? After merely looking at it for a second, we realize the “encryption” involves placing a random character between every other character of the password.

“DECRYPTED”

“DECRYPTED”

Taking another look at our original encrypted password from the batch file we can see it will “decrypt” to: “@D01o$gROup.IO!” which, for the purposes of this writeup, is the shared local administrator password.

Decrypted Original Password

Decrypted Original Password

Another bittersweet moment in information security where, as the riddle is solved, the horrible truth is revealed. Not only is encrypting the password useless in attempting to restrict use of the credentials but the encryption itself is useless in preventing anyone from learning the plaintext of the password. We have seen CTF puzzles designed for children that have provided a greater cryptanalysis challenge than this application.

Diving a bit deeper into the RUNAS_A executable, using ILSpy as RUNAS_A.exe is .NET, we can see that the "encryption" function "EncodePID" involves pairing up random characters with the characters of the password and then base64 encoding the string.

Encryption Function

Encryption Function

The “decryption” function is similarly simple, pulling out every other byte from the “encrypted” password.

Decryption Function

Decryption Function

Ultimately what we have here is a poorly balanced solution and a study in avoidance. There was a need for deploying software, on demand, to end-users. To avoid making every user a local administrator; a single administrator account is reused for every system. To avoid the security restrictions of a commercially available tool; an insecure, in-house application was developed. To avoid the appearance of plaintext credentials within the installation scripts, pointlessly “encrypted” credentials were used. Finally, the golden rule of “Don’t Roll Your Own Crypto” was avoided in spectacular fashion.

Frustratingly to the InfoSec mind, this security train wreck of a solution has been functioning without issue since it was developed and deployed in 2006. The original perpetrators have long since left the organization and, as the application itself has not been a squeaky wheel, it has gotten no security grease.

Hidden issues like these are the sort of findings a penetration test can uncover. Vulnerability scanning will never identify issues like these within a custom application, there will never be a vendor patch or update, and it's too vital an application to just decommission for no reason. A quality penetration test can not only discover problems like this but help demonstrate what the exact impact could be, make the argument as to why things need to change and offer advice on bringing you program back into balance.