Jump to content

Run a Process as Limited User


Anteaus
 Share

Recommended Posts

#Include <StructureConstants.au3>

#cs
RunLimited - replacement for DropMyRights/StripMyRights with enhancements. 
Copyright IWR Consultancy, June 2010, under GPL v3.
- Fixes bug where browsers can't create new folders in userpace.
- Allows loading of Image File Execution entries from an inifile - no more registry hacking. 
- Tags the titlebar, making it easier to tell when a process has been successfully restricted.
Disclaimer: No responsibility accepted for any consequence of use. Any. 
#ce

opt("TrayIconHide",1)

global $baseKey="HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options"
global $debug=0
global $limitedUserRights=""

GetCmdLine()

if $limitedUserRights="undefined" then global $limitedUserRights=IniRead($inifile,"General","LimitedUserRights","Normal")
global $defaultMode=IniRead($inifile,"General","DesktopRights","Admin"); Not yet used

global $debug=IniRead($inifile,"General","Debug",0)

select
 case $mode="I"
  ProcessINI("I")
 case $mode="U"
  ProcessINI("U")
 case $targs<>""
  $p_Handle=SaferLaunchProcess($targs,$limitedUserRights)
  ChangeTitle($p_Handle)
 case else
  ; Possibly add user-interactive mode here
  $WorkingDir=@WorkingDir
  $exeTarget=FileOpenDialog ( "Select a program to Launch as Limited User",@ProgramFilesDir, "Executables|*.exe",3  )
  if @error=0 and $exeTarget <>"" then SaferLaunchProcess($exeTarget)
  FileChangeDir($WorkingDir)
  exit
endselect

exit

func GetCmdLine()
global $inifile=""
$args=$cmdLine[0]
if $args=0 or stringinstr($cmdLineRaw,"/?") then 
  ShowOptions()
  exit
endif
global $targs=""
global $mode=""
if $args=0 then return ""

$initarg=1
 do 
  select
  case stringleft($cmdLine[$initarg],2)="/C" 
    ; Set inifile
    if stringlen($cmdLine[$initarg])=2 then 
       if $initarg<$args then $inifile=$cmdLine[$initarg+1]
       $initarg=$initarg+2
    else 
      $inifile=stringmid($cmdLine[$initarg],4,255)
      $initarg=$initarg+1
      if stringleft($inifile,1)=":" then $inifile=stringreplace($inifile,":","",1)
    endif
    if not FileExists($inifile) then 
      if $inifile="" then $inifile="<Empty Variable>" 
      msg(16,"Error in commandline","Specified configuration-file " & $inifile & " does not exist.",11)
      exit
    endif 
  case stringleft($cmdLine[$initarg],2)="/I" 
    ; Install
    $mode="I"
    $initarg=$initarg+1
  case stringleft($cmdLine[$initarg],2)="/U"
    $mode="U" 
    $initarg=$initarg+1
  case stringleft($cmdLine[$initarg],2)="/L" 
    ; Level of user
    if stringlen($cmdLine[$initarg])=2 then 
     ;Allow for StripMyRights level syntax with space:
     if $initarg<$args then $limitedUserRights=$cmdLine[$initarg+1]
     $initarg=$initarg+2
    else
     $limitedUserRights=stringmid($cmdLine[$initarg],3,255)
     $limitedUserRights=stringreplace($limitedUserRights,":","")
     $initarg=$initarg+1
    endif
  case stringleft($cmdLine[$initarg],2)="/D" 
    ; Debug mode
    ; Note: /D is not necessary with this version but is retained for compatibility.
    $initarg=$initarg+1
  case stringleft($cmdLine[$initarg],1)="/" 
    msg(16,"Unrecognized Command",$cmdLine[$initarg] & " is not a valid commandline item",11)
    $initarg=$initarg+1
  case else
    exitloop
  endselect
 until $initarg>$args

 ; Apply remaining args to target executable:
 for $ct=$initarg to $args
  if $targs = "" then 
   $targs=$cmdLine[$ct]
  else
   $targs &= " " & $cmdLine[$ct]
  endif
 next 

;Get inifile name if none currently set:
if $inifile="" then
 $exename=@scriptname 
 if stringright($exename,4)=".exe" OR stringright($exename,4)=".au3" then 
   global $inifile=stringleft($exename,stringlen($exename)-4) & ".ini"
 else
   global $inifile="RunLimited.ini"
 endif
endif
 return
endfunc


;saferlaunchprocess("c:\program files\seamonkey\seamonkey.exe")

func SaferLaunchProcess($thisProcess,$Trustworthiness="N")

local $SAFER_LEVELID_FULLYTRUSTED = 0x40000
local $SAFER_LEVELID_CONSTRAINED = 0x10000
local $SAFER_LEVELID_NORMALUSER = 0x20000
local $SAFER_LEVELID_UNTRUSTED = 0x01000
local $SAFER_SCOPEID_USER = 2
local $SAFER_LEVEL_OPEN = 1

select 
 case stringleft($Trustworthiness,1)="N"
   $LevelID=0x20000
 case stringleft($Trustworthiness,1)="C"
   $LevelID=0x10000
 case stringleft($Trustworthiness,1)="U"
    $LevelID=0x01000
 case stringleft($Trustworthiness,1)="F"
    $LevelID=0x40000
 case stringleft($Trustworthiness,2)="0x"
    $LevelID=$Trustworthiness
 case else   
    $LevelID=0x20000
endselect


;Open handle
$a_iCall = DllCall("advapi32.dll", "int", "SaferCreateLevel", _
        "dword", $SAFER_SCOPEID_USER, _
        "dword", $LevelID, _
        "dword", $SAFER_LEVEL_OPEN, _
        "hwnd*", 0, _
        "ptr", 0)

If @error Or Not $a_iCall[0] Then
    msg(0,"Error","Failure opening handle occured!",11)
EndIf

$hHandle = $a_iCall[4]

msg(0,"Handle Created","SAFER_LEVEL_HANDLE = " & $hHandle,2)

$a_LCall = DllCall("advapi32.dll", "int", "SaferComputeTokenFromLevel", _
        "dword", $hHandle, _
        "ptr",0, _
        "hwnd*",0, _
        "ptr",0, _
        "ptr",0) 

If @error Or Not $a_iCall[0] Then
    msg(0,"Error","Failure computing Token occured",11)
    return 0
EndIf

$hToken=$a_LCall[3]

$lpCommandline=$thisProcess
$flags=0x00000000
;DLLStructSetData($si,"lpDesktop","winsta0\default")
$lpCurrentDirectory=@WorkingDir
$si=dllstructcreate($tagSTARTUPINFO)
$pi=dllstructcreate($tagPROCESS_INFORMATION)
$pi_size=dllstructgetsize($pi)
$si_size=dllstructgetsize($si)
DLLStructSetData($pi,"Size",$pi_size)
DLLStructSetData($si,"Size",$si_size)

#cs ; Trance
DllCall("advapi32.dll", "bool", "CreateProcessAsUser", _ ; W is better
        "handle", $hToken, _
        "ptr", 0, _ ; you don't need this
        "str", $lpCommandline, _ ; wstr for CreateProcessAsUserW
        "ptr", DllStructGetPtr($lpProcessAttributes), _
        "ptr", DllStructGetPtr($lpThreadAttributes), _
        "bool", $bInheritHandles, _
        "dword", $dwCreationFlags, _
        "ptr", 0, _ ; you don't need this
        "ptr", 0, _ ; you don't need this
        "ptr", DllStructGetPtr($si), _
        "ptr", DllStructGetPtr($pi))
#ce 

$thisPKey=filename($thisProcess)
$dbgValue=RegRead($baseKey & "\" & $thisPKey,"Debugger")
$rluValue=RegRead($baseKey & "\" & $thisPKey,"RunAsLimitedUser")
msg(0,"Debugger",$rluValue & " > " & $thisPKey & " > " & $dbgValue,1)

if $rluValue>0 AND $dbgValue <>"" then 
 RegDelete($baseKey & "\" & $thisPKey,"Debugger")
endif

$a_PCall = DllCall("advapi32.dll", "bool", "CreateProcessAsUserW", _
        "ptr", $hToken, _
        "ptr", 0, _
        "wstr",$lpCommandline, _
        "ptr", 0, _
        "ptr", 0, _
        "bool",False, _
        "dword",$flags, _
        "ptr", 0, _
        "ptr", 0, _
        "ptr", dllstructgetptr($si), _
        "ptr", dllstructgetptr($pi))

;       "wstr",$lpCurrentDirectory, _
          
         $dllerr=@error

if $rluValue>0 then 
  RegWrite($baseKey & "\" & $thisPKey,"Debugger","REG_SZ",$dbgValue)
endif    
         $rtn=""
;         for $ct=0 to 11
;         $rtn &= $ct & " : " & $A_PCall[$ct] & @lf
;         next

for $ct=1 to 4
         $rtn &= "pi" & $ct & " : " & DllStructGetData( $pi, $ct ) & @lf
next 
         
 msg(0,"Process Info" & $dllerr,$rtn,5)


if $a_Pcall[0]>0 then
 $pHandle=DllStructGetData( $pi, 3 )
else
 $pHandle=0
endif

       
;Close handle
$a_iCall = DllCall("advapi32.dll", "int", "SaferCloseLevel", _
        "hwnd", $hHandle)

If @error Or Not $a_iCall[0] Then
    msg(0,"Error","Failure closing handle occured!",1)
EndIf

; Release allocated memory: 
$pi=""
$si=""
$lpThreadAttributes=""
$lpProcessAttributes=""

;ConsoleWrite("Handle succesfully closed" & @CRLF)

return $pHandle

endfunc


func setLimitedRun($limitedApps,$policyDebugger=@scriptfullpath)

if stringright($policyDebugger,4)=".au3" then 
msg(16,"Inadvisable action","Setting or modifying the debug option with an interpreted script is not advised, as it may lead to trouble. Compile as an executable before taking this action",11)
endif

$lApps=stringsplit($limitedApps,",")

local $ct=0
local $idx=0
local $ifos[100][3]
While 1
    $idx+=1
    if $idx=ubound($ifos) then redim $ifos[$idx+10][0]
    $thisifo = RegEnumKey($baseKey, $idx)
    If @error <> 0 then 
      $ifos[0][0]=$ct
      ExitLoop
    endif
    if $thisifo="DLLNXOPTIONS" or stringinstr($thisifo,"Your Image File")=1 THEN
      continueloop
    endif
    $thisDebugger=RegRead($baseKey & "\" & $thisifo,"Debugger")
    if $thisDebugger="" then continueloop
    $ct+=1
    $ifos[$ct][0]=$thisifo
    $ifos[$ct][1]=$thisDebugger

$var=""
For $i = 1 to 100
$v = RegEnumVal($baseKey & "\" & $thisifo, $i)
if @error <> 0 Then ExitLoop
$var = $var & "|" & $v
next
if $var<>"|" then $var=$var & "|"
$ifos[$ct][2]=$var

Wend
;0 = this exe
;1 = debug app
;2 = value list

$out=""
for $ct=1 to $ifos[0][0]
 $out = $out &  $ifos[$ct][0] & "->" & $ifos[$ct][1] & $ifos[$ct][2]& @cr
next
msg(0,"imagefiles",$out,1)

for $ct=1 to $ifos[0][0]
; Delete ifos no longer required.
if stringinstr($limitedApps,$ifos[$ct][0]) = 0 then 
 $isPolicyItem=RegRead($baseKey & "\" & $ifos[$ct][0],"RunAsLimitedUser")
 if stringinstr(RegRead($baseKey & "\" & $ifos[$ct][0], "Debugger"),"MyRights")>0 then $isPolicyItem=1
 if $isPolicyItem=1 then
    msg(0,"delitemstrings",$ifos[$ct][2],2)
   if ($ifos[$ct][2]="|Debugger|RunAsLimitedUser|") then 
    msg(0,"deletefully",$ifos[$ct][0],2)
     if RegRead($baseKey & "\" & $ifos[$ct][0], "Debugger")<>"" then RegDelete($baseKey & "\" & $ifos[$ct][0])
   else
     msg(0,"delete",$ifos[$ct][2],2)
     RegDelete($baseKey & "\" & $ifos[$ct][0], "Debugger")
     RegDelete($baseKey & "\" & $ifos[$ct][0], "RunAsLimitedUser")
   endif
 endif
endif 
next

for $ct=1 to $lApps[0]
 if $lApps[$ct] <> "" then 
 $PrevDebugger=RegRead($baseKey & "\" & $lApps[$ct],"Debugger")
   if $prevDebugger<>$policyDebugger then 
     msg(0,$prevDebugger,$policyDebugger,3)
     RegWrite($baseKey & "\" & $lApps[$ct],"Debugger","REG_SZ",$policyDebugger) 
     RegWrite($baseKey & "\" & $lApps[$ct],"RunAsLimitedUser","REG_DWORD","1") 
    msg(0,"Updated",$baseKey & "\" & $lApps[$ct] & " " & @error,3)
   endif
 endif
next

$lrAction="Updated"
if $limitedApps="" then $lrAction="Removed"
msg(64,"Settings Updated","The specified LimitedRights entries have been " & $lrAction)

endfunc

func Processini($action="I")
 $lApps=""
 if $action="I" then 
  $iniApps=IniReadSection($inifile, "RunLimited" )
  if @error or $iniApps[0][0]=0 then 
   $limitedApps=""
  else
   For $ct=1 to $iniApps[0][0]
     if $lApps="" then 
      $lApps = $iniApps[$ct][1] 
     else
      $lApps &= "," & $iniApps[$ct][1] 
    endif    
   next
  endif
 endif
 setLimitedRun($lApps)
endfunc 

func filename ($_pathtostrip)
 return stringmid($_pathtostrip,stringinstr($_pathtostrip,"\",0,-1) +1 ,255)
endfunc


func msg($_icon,$_title,$_text,$_mode=0)
; mode 0 general message
; mode 1-9 debug levels
; mode > 10 terminal error

$_timeout=30
$_opts=0
if $_mode < 10 then $_opts=4
if $_mode=0  then $_opts=0
if ($debug - $_mode >= 0) OR ($_mode>10)  then 
  $_msg=msgbox($_icon + $_opts,$_title,$_text,$_timeout)
  if $_msg=7 or $_opts=0 then exit
endif
endfunc

func ChangeTitle($_pHandle)
if $_pHandle=0 then return

for $_ct=1 to 100
 sleep(100)
 $_thispid=WinGetProcess("[ACTIVE]")
  ; msgbox(0,"pids",$_thisPID & " " & $_phandle,5)
   if $_thispid=$_pHandle then 
   $_newtitle=WinGetTitle("[ACTIVE]","") & ": Limited User"
   WinSetTitle("[ACTIVE]","",$_newtitle)
   exitloop
 endif
next

endfunc

func ShowOptions()
msgbox(64,"RunLimited - Run a program with reduced user-rights","RunLimited [Options] Program.exe [Parameters]" & @crlf & @crlf & "Options:" & @crlf & "/C:settings_file.ini - Load custom settings" & @crlf & "/I /U -Install/Uninstall settings" & @crlf & "/L:[NCUF] Privelege Level OR" & @crlf & "/L:0xnnnnn Privelege identifier in hex",120)
endfunc

If you've encountered DropMyRights or StripMyRights you'll know what this is for. Basically, if you're one of those live-dangerous bods who logs-on as an Admin, you can prepend RunLimited.exe to the commandline of your browser or email prog, and it's launched as a limited process which (given correct NTFS permissions) can only write to folders in userspace. This greatly reduces the risk of Internet malware being able to gain a foothold.

Includes a few enhancements, notably that if you wish to use the ImageFileExecution method of hooking processes, you can do this with an .ini file instead of having to hack the registry. Just add the progs you want to restrict to RunLimited.ini like so:

[RunLimited]
Mozilla Firefox=firefox.exe
Seamonkey=Seamonkey.exe
Opera=opera.exe
Internet Liability=iexplore.exe

and run Runlimited /i to install these values. Note that once installed like this you should not move or delete RunLimited.exe without first running it with the /u switch to unset these values, or the affected programs will no longer launch.

You don't of course need to do any of the above for a simple test. Just compile with 3.2.6.1 and run

RunLimited <path\program.exe>

to create a limited process.

So far only tested on XP Pro/32, and still in development so use with care.

BTW, thx to Trancexxx for help with system-calls.

Edited by Anteaus
Link to comment
Share on other sites

Hi Anteaus

Script is looking good.

Just shows that Debuggers can be put to good use! But, it does go both ways.

AutoIt is reporting this error when browser is clicked on ...

post-55206-12761300619386_thumb.png

#Include <StructureConstants.au3>

.

Edited by ripdad

"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Link to comment
Share on other sites

#Include <StructureConstants.au3>

Oops, that's what happens when you decide to 'tidy things up a bit' :mellow:

Well spotted.

On the subject of debuggers and malware, it did occur to me to have it clear all unidentified debug hooks, regardless of what set them. Not sure if this might annoy some coders though.

Edited by Anteaus
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...