Batch script to run as both regular user and admin

Discussion in 'Scripting' started by sacarias, Jul 13, 2019.

  1. sacarias

    sacarias MDL Junior Member

    Nov 21, 2018
    82
    1
    0
    I need to make a batch script to some some tasks: write and modify some few configurations in %userprofile% (unprivileged user), and query some registry info which needs administrator rights.

    The thing is, if I right click the script and run it as administrator, all stuff will be run as the *administrator* user (whoever it is) instead of my currently logged in %username%, and thus all changes in %userprofile% will be for the administrator user instead of the current one.

    Also, I can't just "run as <particular_username>" because I need to use the script in different PCs, so I do need to use a variable.

    First easy obvious option would be just splitting it into 2 different scripts, one for user and one to be run as admin. But I'd really like to make it in one single script if possible.

    I have tried to imagine possible "solutions": run the script as admin and somehow get current logged in %username%, or run as regular user and somehow automatically ask for admin privileges once reaching the part which needs it.
    But I don't know how to do any of them.

    Could someone help please?
    Thanks very much in advance.

    @abbodi1406:
    Hope you can see this if you have chance (and time...)
     
  2. abbodi1406

    abbodi1406 MDL KB0000001

    Feb 19, 2011
    16,141
    84,317
    340
  3. AveYo

    AveYo MDL Expert

    Feb 10, 2009
    1,836
    5,685
    60
    You could self-elevate the script on your own terms, passing the arguments you need like in this example:
    Code:
    @echo off
    
    :: grab current user's SID [string-safe in scripts unlike using the name]
    for /f "tokens=2" %%s in ('whoami /user /fo list') do set CU=%%s
    
    :: set arguments to pass userprofile and currentuser above and any other command line parameters to self-elevate via powershell
    set "args=set "__UP__=%USERPROFILE%" &set "__CU__=%CU%" &set ?=y&call "%~f0" %*" &call set "args=%%args:"=\""%%"
    
    ::AveYo: self-elevate passing args and preventing loop
    reg query HKU\S-1-5-20 >nul 2>nul || if "%?%" neq "y" (powershell -c "start cmd -ArgumentList '/c %args%' -verb runas" &exit)
    :: should be elevated here
    
    echo [CURRENT USER] %__UP__%
    wmic useraccount where sid='%__CU__%' get name,sid /format:table
    
    if not defined __CU__ echo .. %%__CU__%% is not available because you've right-click Run as administrator &echo. &echo.
    
    echo [RUN AS ADMIN] %USERPROFILE%
    wmic useraccount where sid='%CU%' get name,sid /format:table
    
    pause
    exit/b
    Or group actions based on required rights, and only elevate when necessary like in this example:
    Code:
    @echo off
    
    if defined __JUMP__ goto :%__JUMP__%
    
    :peasant
    echo doin' stuff not requiring admin rights
    whoami /user /fo list
    echo.
    rem pause
    
    echo asking for admin rights...
    
    :: set arguments to jump to :admin label and pass any other command line parameters to self-elevate via powershell
    set "args=set __JUMP__=admin &set ?=y&call "%~f0" %*" &call set "args=%%args:"=\""%%"
    ::AveYo: self-elevate passing args and preventing loop
    reg query HKU\S-1-5-20 >nul 2>nul || if "%?%" neq "y" (powershell -c "start cmd -ArgumentList '/c %args%' -verb runas" &exit)
    :: should be elevated here
    
    goto :admin
    exit/b
    
    :admin
    echo doin' stuff requiring admin rights
    whoami /user /fo list
    echo.
    pause
    exit/b
    
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  4. TigTex

    TigTex MDL Senior Member

    Oct 5, 2009
    450
    356
    10
    I do this on my scripts. Because it doesn't use powershell, works with windows vista and later operating systems
    Code:
    
    INSERT HERE THE UNPRIVILEGED USER CODE
    
    (
        >nul fsutil dirty query %SYSTEMDRIVE% 2>&1 || (
            :: Create VBS script
            echo Set UAC = CreateObject^("Shell.Application"^)>"%TEMP%\elevate.vbs"
            echo UAC.ShellExecute "%~f0", "%TEMP%\elevate.vbs", "", "runas", 1 >>"%TEMP%\elevate.vbs"
            if exist "%TEMP%\elevate.vbs" start /b /wait >nul cscript /nologo "%TEMP%\elevate.vbs" 2>&1
            :: Delete elevation script if exist
            if exist "%TEMP%\elevate.vbs" >nul del /f "%TEMP%\elevate.vbs" 2>&1
            exit /b
        )
    )
    
    INSERT HERE THE CODE THAT NEEDS ADMIN PERMISSIONS
    
    
    Maybe it helps you
     
  5. sacarias

    sacarias MDL Junior Member

    Nov 21, 2018
    82
    1
    0
    Thanks very much everyone for your advise.

    Certainly, what I'm actually seeking is grouping actions based on required rights; but also that actions done by regular user remain regular user's (in terms of file ownerships, permissions, stamps, etc), and admin actions remain admin's.

    So with that in mind, I have this:
    Code:
    @echo off
    
    %windir%\system32\reg.exe query "HKU\S-1-5-19" > nul 2>&1 && (
    REM assuming user right-clicked -> run as admin
    echo ERROR: you must not run as admin
    echo.
    echo Press any key to exit...
    pause > nul
    goto :eof
    )
    
    REM doing stuff as non-privileged user
    [...]
    
    (
            %windir%\system32\reg.exe query "HKU\S-1-5-19" > nul 2>&1 || (
    
            REM Create VBS script
            echo Set UAC = CreateObject^("Shell.Application"^) > "%TEMP%\elevate.vbs"
            echo UAC.ShellExecute "%~f0", "%TEMP%\elevate.vbs", "", "runas", 1 >> "%TEMP%\elevate.vbs"
            if exist "%TEMP%\elevate.vbs" start /b /wait > nul cscript /nologo "%TEMP%\elevate.vbs" 2>&1
    
            REM Delete elevation script if exist
            if exist "%TEMP%\elevate.vbs" > nul del /f "%TEMP%\elevate.vbs" 2>&1
            exit /b
        )
    )
    
    REM doing stuff as privileged user
    [...]
    
    echo Process complete
    echo Press any key to finish...
    pause > nul
    Maybe I'm wrong with the idea, advise always welcome...

    I tested both options from both @BAU (his second one) and @TigTex.
    Both do the job indeed, albeit I noticed they work by opening a brand new CMD session for the admin user alone once permission has been granted. For what I'm seeking this has a downpoint: this new session runs whole script from beginning again, and since script tells not to run as admin, the job is naturally never completed.

    Opening a new CMD session window necessarily, is this the only way it works? Or could there be a workaround?

    Another option I thought about was just using labels for each case, regular or admin, and goto corresponding label according to case. This would imply running script twice indeed, though at least better than having 2 scripts...
     
  6. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,440
    1,420
    60
    You could pass a parameter. Change
    Code:
    echo UAC.ShellExecute "%~f0", "%TEMP%\elevate.vbs", "", "runas", 1 >> "%TEMP%\elevate.vbs"
    by
    Code:
    echo UAC.ShellExecute "%~f0", "%Params%", "", "runas", 1 >> "%TEMP%\elevate.vbs"
    where %Params% represents parameters you pass to the script. Test for the existence of the parameter at the begining of the script.
     
  7. sacarias

    sacarias MDL Junior Member

    Nov 21, 2018
    82
    1
    0
    #7 sacarias, Jul 16, 2019
    Last edited: Jul 16, 2019
    (OP)
    Sorry, er... I'm actually a novice in all this matter and what I have done until now was based on limited knowledge and a lot of internet searches.
    Could you perhaps elaborate a bit more please?

    Like putting all stuff meant to be run as admin somewhat inside a %params% variable, or something like that?

    Thanks.

    PS: by the way, would all what I'm seeking still be "orthodox" (stable for work most of times), or am I actually going too "hacky" already and I better run the script twice?
     
  8. AveYo

    AveYo MDL Expert

    Feb 10, 2009
    1,836
    5,685
    60
    #8 AveYo, Jul 17, 2019
    Last edited: Jul 31, 2019
    It does not happen on my example - script runs first part as user, and just second part as admin - that's what the jump stuff is there for.
    It's the core directive behind elevation - you must spawn another process somehow! No workaround - by design.

    Anyway, it seems there is a lack of proper underlying user detection that I'm going to solve right here:
    Code:
    @echo off
    
    REM IT SHOULD NOT MATTER IF THE USER HAS RIGHT-CLICKED RUN AS ADMIN THE SCRIPT OR NOT
    
    ::AveYo: self-elevate passing args and preventing loop
    set "args="%~f0" %*" & call set "args=%%args:"=\""%%"
    reg query HKU\S-1-5-19>nul||(if "%?%" neq "y" powershell -c "start cmd -ArgumentList '/c set ?=y&call %args%' -verb runas" &exit)
    
    REM NOW DEFINITELY RUNNING AS ADMIN - EITHER WAY
    
    ::AveYo: get Domain\User irrespective of script being run as Admin - another of my neat tricks :)
    for /f tokens^=^13^ delims^=^" %%u in ('tasklist /fi "imagename eq explorer.exe" /fo csv /v /nh') do if "%%u" neq "," set "DU=%%u"
    echo Domain\User: %DU%
    
    ::AveYo: simple replacement to get just the User
    call set "CU=%%DU:%COMPUTERNAME%\=%%"
    echo User: %CU%
    
    ::AveYo: get SID for User - can be input in my reg_takeownership snippet or icacls instead of domain\name
    for /f "tokens=1" %%s in ('wmic useraccount where ^(name^="%CU%" and domain^="%COMPUTERNAME%"^) get sid /value') do set "%%s">nul
    echo SID: %SID%
    
    ::AveYo: and the Domain\User running as admin being:
    echo Domain\User (Elevated): %COMPUTERNAME%\%USERNAME%
    
    echo.
    
    ::AveYo: now let's put it to a test with a real-life example
    takeown /f "C:\Program Files\Windows NT\Accessories\*" /r /skipsl /a
    icacls "C:\Program Files\Windows NT\Accessories\*" /grant:r "%DU%":F
    
    pause
    exit
    
    
    So with this method at your disposal, it no longer matters at which point you decide to elevate the script yourself, as you can determine the underlying user if the script was right-clicked - run as admin.
    The most convenient is at the beginning, but you can also group actions by rights (properly this time) as in my 2nd example, and then ask for elevation just for the admin part (and can make that window hidden - just google powershell start-process)
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  9. sacarias

    sacarias MDL Junior Member

    Nov 21, 2018
    82
    1
    0
    @BAU:
    Thanks yet again for all your advise.
    I'll double-check the options again.

    Finally, and just curious, do you think it could be possible to do without powershell, i.e., only with cmd stuff?

    Thanks again.
     
  10. AveYo

    AveYo MDL Expert

    Feb 10, 2009
    1,836
    5,685
    60
    there are not that many other ways to elevate (mshta/rundll32/jscript/vbscript/schtasks/wmic) and most have uglier syntax than the powershell one-liner, and the show-stopper is that anti-virus software including built-in defender flag those other alternatives as malicious.
    use powershell - it's more convenient and more compatible (comes with windows since 7) and all chances are it wont be blocked by some lame av or sys admin even before reaching your users
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  11. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,440
    1,420
    60
    I believe powershell is the way to go. With vbscript you may encounter issues when directory names include special or foreign characters.
    @BAU : great scripting!
     
  12. presto1234

    presto1234 MDL Novice

    Nov 16, 2015
    37
    49
    0
    #12 presto1234, Jul 19, 2019
    Last edited: Jul 19, 2019
    Hi, I think you can use this script https://forums.mydigitallife.net/th...ts-and-special-chars-incl-quote-no-tmp.74332/
    If you embed the VBS part using the "elevate_no-arguments" code and embed it as described, you can run the script without ever making any temporary files (write is a bottleneck if you think about it) and the thing looks Waaaaaay cleaner.
    If you use the normal VBS elevate code, you can even use some special characters that normally cause VBS to stop working, like quotes and others.

    Ofcourse Powershell is more functional, but unless a very sensitive AV or system administrator blocks VBS, CMD or embedded scripts, in 80% of the other cases this should run nearly everywhere, should work even on XP.

    You could use the script like this:
    Code:
    <!-- : Begin batch script
    @ECHO OFF
    
    FSUTIL dirty query "%SYSTEMDRIVE%" >NUL && (
      call :AdminPart
    ) || (
       ECHO(*************************************
       ECHO(Invoking UAC for Privilege Escalation
       ECHO(*************************************
       CSCRIPT //nologo "%~f0?.wsf" //job:ELAV /File:"%~f0"
       call :UserPart
    )
    
    echo(=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    echo( This code is run as either Admin or User
    echo( I'll just add a line to make things look neat
    echo(
    echo(Below are all the environment variables
    echo(
    set
    echo(=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    pause
    exit /b
    
    :AdminPart
    echo( Only runs as Admin and stuff
    echo( I run as %USERNAME% and my userprofile is %userprofile%
    exit /b
    
    :UserPart
    echo( Only runs as current user and stuff
    echo( I run as %USERNAME% and my userprofile is %userprofile%
    exit /b
    
    ----- Begin wsf script --->
    <package>
       <job id="ELAV">
          <script language="VBScript">
             Set strArg = WScript.Arguments.Named
             If Not strArg.Exists("File") Then
                Wscript.Echo "Switch /File:<File> is missing."
                WScript.Quit 1
             End If
             CreateObject("Shell.Application").ShellExecute strArg("File"), ELAV, "", "runas", 1
          </script>
       </job>
    </package>
     
  13. sacarias

    sacarias MDL Junior Member

    Nov 21, 2018
    82
    1
    0
    Sorry for bringing this back...

    I'm already taking your useful advises. But one thing came to mind: when elevating to privileged user, what if not successfully elevated?
    I mean, what if for some reason admin's password is not successfully entered? Commonly because forgotten password and need time to remember, but having to hit Cancel and stopping the script.

    Is there a way in CMD (not powershell) to display messages or do stuff in case admin password is not successfully entered?
     
  14. rayleigh_otter

    rayleigh_otter MDL Expert

    Aug 8, 2018
    1,121
    933
    60
    #14 rayleigh_otter, Nov 6, 2019
    Last edited: Nov 6, 2019
    Get Power Run from Sordum, drag a .reg .bat .cmd to its UI and double click. Everything gets elevated and its never failed yet. :)

    Sometimes those files run so fast somethings get skipped, add a 1 second timeout between commands, its slower and hasnt failed yet :cool:. "hurry burry spoils the curry".
     
  15. sacarias

    sacarias MDL Junior Member

    Nov 21, 2018
    82
    1
    0
    Let me try again.

    Is there any way to validate whether admin password was successfully entered or not?