Run a program as user identity but with elevated privileges
Asked Answered
I

4

10

Scenario:

An administrator will install the application. The application has some kernel level operations so, it has to run with privileged mode. But the user does not have administrator credentials to run the application in elevated mode.

So, what are the best possibility to solve the above scenario.

Solution one (tried): While installing the application through administrator, we would create an admin where we know his user name and password. So, when the user tries to perform any operation, we will run the application as elevated mode using the functions processstartinfo() and process.start() in c#. But, the application runs in admin name, so the mapped drives for the user are not visible as this admin is not in the ACL list. So, this method is getting ruled out.

Can there be a solution where elevating the user it self as admin till the operation is completed using c# application?

I'm stuck and I don't find any articles to read upon on this.

Edit : At an enterprise level, the windows doesn't ask for admin creds for using bitlocker. So, we want to achieve the same type of functionality.

Is it possible?

Can anyone please help?

Thank you.

Ikon answered 31/12, 2018 at 6:45 Comment(23)
"where we know his user name and password" - and any hacker would easily know them too.Evans
The more secure solution would be to install a service and let the main app make requests there. That would not solve 'drive mapping', can't you use unc names?Evans
When you just want a light protection against the user himself, look into the Impersonation API. I think it leaves the base User's environment intact.Evans
@HenkHolterman Yes, it would be vulnerable leaving username and password. Acutally the product has to be tuned according to the requirement of the enterprise. So, they said they have ACL list, so if the app is not running as the user it can't access the related mapped drives. Impersonating is same like createprocesswithlogonw function, but the application runs on user name or admin name while impersonating and will it have elevated privileges as the operation needs them?Ikon
One more over head is, if we elevate the user itself as admin at least for the time that function has to run, the user can run any malicious script in the enterprise with admin credentials. That would also be a problem. But, we want to deal with this problem later.Ikon
If we are elevate the user only for that application as the user identity himself, then that should solve all these scenarios. I believe.Ikon
Is it required that application should run in logged in user context, or is it fine if it runs in a common administrator account. Then you can try Windows impersonation using LogonUser API. Your application will start in logged in user context and during run time application will change user context to administrator account. For this you need to store admin account details securely which will be access by your application during run time for impersonation.Perfection
If I'm impersonating, the application cannot access any of the user drives in the network as the admin is not part of the ACL list for that drive. I've tried this method using CreateProcessWithLogOnW function. The problem is that from application we have browse option where he is able to browse the admin files which should not happen. So, if we can run the user with admin elevation, the browse and network drives can be of the user itself. Is it possible?Ikon
@SusarlaNikhilesh can you edit the app source code ? or you are using outscored , ready made softwarePankey
I can edit the source code. We can edit it at both application and kernel level, but we are looking for application level solution.Ikon
You could create two applications: App1 that runs as admin (as a service or background exe), and App2 that runs as the user. Both can be installed and configured by admin at setup time. When App2 needs to do some elevated stuff, it communicates with App1 though an authentified mechanism (named pipe for example), so App1 checks the calling user using built-in Windows mechanisms (but it doesn't impersonate) and does what's asked to do. You can code App1 as a standard app or as a COM out-of-process server.Sulphuric
So, can I create App1 as windows service project? and App2 is a normal c# application. Yes, I also see that's the only way, can you please give me more resources regarding the named pipe and COM out-process/Ikon
I have managed to create a service and call it from the application. But now, I'm looking for a way to call my own functions other than onStart etc. I have seen onCustomCommand function and from there I can call my own functions. one more concern is that I have to pass a bunch of parameters to my own functions. But, onCustomCommand only excepts one parameter and user CAN'T start a service, as onStart takes string args[]. Can you let me know any other way possible ?Ikon
see https://mcmap.net/q/377113/-how-to-communicate-with-a-windows-servicePankey
The start, stop, etc. commands are for communicating with the Windows Service Manager (SCM). But for communicating with another app, you must add another piece of code that would implement IPC (inter process communication), probably on other thread, etc. There are many ways to do IPC between two apps with authentication, for example: Web/HTTP, Named Pipes, COM, WCF. The COM way is the one I would prefer personally because it's just a matter of calling remote interface(s). It depends on the things you want to do. PS: don't forget to append @ login to address a comment, or we're not notified.Sulphuric
You may try to temporarily add user to the admin group (you'll need some admin login/pwd, but the user doesn't have to be an admin for that), run the command and remove from the admin group. This will keep the user identity.Vocalic
@Tom Yes, I tired that method, but the change only takes effect only after the user next signin. So, say suppose he signs out and signs in, then the user is give full admin rights until he opens our application where we would remove his name from the admin group. But to get that into effect, he has to sign out again.Ikon
@SimonMourier we are not thinking to write another app. Instead write a windows service. Where the user app will communicate with windows service. The functions that require the elevation will be put inside the windows service. As windows service will be running as elevated. But the only thing is that we have to look for a way to pass the parameters required for our function in the service from the app.Ikon
@SimonMourier I have looked for options, I see only WCF is a way to call user functions in service. I'm new to .net can you please provide any good understandable examples to proceed on or are there any ways to do?Ikon
Service = app. I'm not sure a standard Windows Service is the best way. Using COM is the easiest and most natural way to pass parameters (it's just interfaces, code). You can host a COM dll as an out-of-process authenticating app (call it a "service" if you like) easily with COM Component Services/COM+ (it's a surrogate host) as it has builtin RBAC or other security mechanisms: learn.microsoft.com/en-us/windows/desktop/cossdk/… plus its installation can be done programmatically too. It can provide a sample for COM in .NET C#.Sulphuric
@SimonMourier Thank you. I've got a small doubt, can we add a .dll to windows service and install the service and call the functions in dll using application. What I'm assuming is as windows service runs in admin level so that the dll also will run in admin level. Does this work ?Ikon
Why not run your application in two seperate parts? The first does the managing of admin level access as a service, which then communicates with the second application which runs under normal priviledges.Handcuff
@Handcuff Yes, we thought of the same as to run the functions inside the service. But, we can't pass parameters to service from application. So, as "SimonMourier" said we are trying to use named pipes to communicate between service and applicationIkon
G
3

The solution that I did was to separate the program into two parts. One part runs as a service that is run using admin privileges and the other part runs using normal privileges. The communication between the two programs can run via Ethernet or using shared memory.

Grantee answered 7/1, 2019 at 16:18 Comment(3)
can we run dll as a service? and pass parameters to the dll when running as a service?Ikon
A service is always an executable. See #4451716 to communicate with the service.Grantee
I have already looked into the above link. I'm looking for a way(example), where as to communicate with the service which has the dll binded using interprocess like pipes/wcf/coms.Ikon
P
0

I think you might be stuck because you are dealing with two functions that have permissions features, and both are required to properly run your application.

I have noted that you have these two:

  • File System Path. Only the logged-on user has access to this (the admin account does not).
  • Application Processes. Only the admin account can run these processes (the logged-on user will be prompted with user elevation).

To illustrate this...

        |  File System Path   |  Application Process
User    |        OK           |          --
Admin   |        --           |          OK

The solution needs to look like this...

        |  File System Path   |  Application Process
Service |        OK           |          OK

If possible, I would say this is the most straight-forward way, whereby you create a service account that has permissions to do both.

Pastore answered 8/1, 2019 at 9:11 Comment(4)
but service can't have custom functions where the application can call !Ikon
@SusarlaNikhilesh why not?Pastore
You mean create a windows service, add custom functions like add, subtract and install the service at admin level , so that service runs as admin. From user application which does not admin privileges can access these functions in service?Ikon
I think you need to read this about Service AccountsPastore
W
0

You could try creating a parallel thread that has admin privileges in order to execute the kernel operations. This way your whole program is contained in one executable.

Witchery answered 9/1, 2019 at 23:55 Comment(2)
You cant do this. The privileges apply according to the account running the Process.Hendeca
@Ben how can a non user mode application launch a thread with admin credentials? I'm not sure if it is possible. Please let me know if it is possible but should not lose the user identity.Ikon
S
0

What you can do is use COM+ Component Services. With .NET the easiest way is use Enterprise Services's ServicedComponent which has all sorts of wrappers and utility classes to interop with COM+ Component services.

So here are steps to do it:

1) Create a .NET Framework Class Library.

2) Add it a strong name and sign it with it

3) Add it a class like this for example (I've also put some utility method to diagnose things)

[ComVisible(true)]
public class AdminClass : ServicedComponent
{
    public int DoSomethingAsAdmin()
    {
        // test something that a normal user shouldn't see
        return Directory.GetFiles(Path.Combine(Environment.SystemDirectory, "config")).Length;
    }

    public string WindowsIdentityCurrentName => WindowsIdentity.GetCurrent().Name;
    public string CurrentProcessFilePath => Process.GetCurrentProcess().MainModule.FileName;

    // depending on how you call regsvcs, you can run as a 32 or 64 bit surrogate dllhost.exe
    public bool Is64BitProcess => Environment.Is64BitProcess;
}

4) Add the following to AssemblyInfo.cs

[assembly: ApplicationName("AdminApp")]
[assembly: SecurityRole("AdminAppUser")]
[assembly: ApplicationActivation(ActivationOption.Server)]

What this does is define a COM+ application named "AdminApp", add a role named "AdminAppUser" to it, and declare the app will run as a "server" which means "out-of-process".

5) Compile that and run this command as admin

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regsvcs.exe AdminApp.dll

or this command:

C:\Windows\Microsoft.NET\Framework\v4.0.30319\regsvcs.exe AdminApp.dll

Both commands will create the the COM + application, and host the .NET library DLL in a surrogate .exe (dllhost.exe). If you choose the first, the hosted process will run as x64, and if you run the second, the hosted process will run as x86.

You can check the result of this registration if you run Component Services (from Windows/Run):

enter image description here

6) Right-click the app and you'll see a whole bunch of cool things you can configure. Note you can even run this as a service (in the 'Activation' tab), etc. What you must do is configure the identity which will run this process, something like this:

enter image description here

Here, I've used a custom admin account. You don't want to use any of the other builtin choices.

7) Now, since default security has been enabled, basically nobody can calls this component. So we just have to add a user to the role "AdminAppUser" we created earlier. You can of course do this using the UI as shown here:

enter image description here

but here is a piece of code that does this programmatically (we use the COM+ administration objects) :

AddUserInRole("AdminApp", "AdminAppUser", @"SMO01\simon");

....

static void AddUserInRole(string appName, string roleName, string userName)
{
    dynamic catalog = Activator.CreateInstance(Type.GetTypeFromProgID("COMAdmin.COMAdminCatalog"));

    // the list of collection hierarchy : https://learn.microsoft.com/en-us/windows/desktop/cossdk/com--administration-collections
    var apps = catalog.GetCollection("Applications");
    var app = GetCollectionItem(apps, appName);
    if (app == null)
        throw new Exception("Application '" + appName + "' was not found.");

    var roles = apps.GetCollection("Roles", app.Key);
    var role = GetCollectionItem(roles, roleName);
    if (role == null)
        throw new Exception("Role '" + roleName + "' was not found.");

    // UsersInRole collection
    // https://learn.microsoft.com/en-us/windows/desktop/cossdk/usersinrole
    var users = roles.GetCollection("UsersInRole", role.Key);
    var user = GetCollectionItem(users, userName);
    if (user == null)
    {
        user = users.Add();
        user.Value["User"] = userName;
        users.SaveChanges();
    }
}

static dynamic GetCollectionItem(dynamic collection, string name)
{
    collection.Populate();
    for (int i = 0; i < collection.Count; i++)
    {
        var item = collection.Item(i);
        if (item.Name == name)
            return item;
    }
    return null;
}

The result should be like this:

enter image description here

8) Now, for the client app, using the AdminApp facilities is easy. Don't reference the .DLL as a standard .NET reference, but use it as any other external COM component. You could reference the .TLB file that was created by regsvcs, or just use the magic dynamic keyword as I demonstrate here (the drawback is you don't get autocompletion):

using System;
using System.Security.Principal;

namespace UserApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Is64BitProcess " + Environment.Is64BitProcess);
            Console.WriteLine("Running As " + WindowsIdentity.GetCurrent().Name);

            var type = Type.GetTypeFromProgID("AdminApp.AdminClass");
            dynamic trustedClass = Activator.CreateInstance(type);

            Console.WriteLine("Admin App Process Path: " + trustedClass.CurrentProcessFilePath);
            Console.WriteLine("Admin App Running As: " + trustedClass.WindowsIdentityCurrentName);
            Console.WriteLine("Admin App Is64BitProcess: " + trustedClass.Is64BitProcess);
            Console.WriteLine("Admin App DoSomethingAsAdmin: " + trustedClass.DoSomethingAsAdmin());
        }
    }
}

Now, when you run it for example as "simon", you should see something like this, it works:

Is64BitProcess False
Running As SMO01\simon
Admin App Process Path: C:\WINDOWS\system32\dllhost.exe
Admin App Running As: SMO01\myAdmin
Admin App Is64BitProcess: True
Admin App DoSomethingAsAdmin: 71

and when you run it for example as "bob" who's not configured in the role, you should see something like this with an access denied, this is expected:

Is64BitProcess False
Running As SMO01\bob

Unhandled Exception: System.UnauthorizedAccessException: Retrieving the COM class factory for component with CLSID {0DC1F11A-A187-3B6D-9888-17E635DB0974} failed due to the following error: 80070005 Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)).
   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
   at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at System.Activator.CreateInstance(Type type)
   at UserApp.Program.Main(String[] args) in C:\Users\simon\source\repos\TrustedSystem\UserApp\Program.cs:line 14

Note we've created a trusted system without setting any password anywhere. And, I've only scratched the surface of what you can do with COM+ component. For example, you can export the app as an .MSI for easy deployment, etc.

Sulphuric answered 10/1, 2019 at 11:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.