Skip to content

Instantly share code, notes, and snippets.

@lukesampson
Last active November 15, 2019 03:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lukesampson/9130445 to your computer and use it in GitHub Desktop.
Save lukesampson/9130445 to your computer and use it in GitHub Desktop.
Debug script for sudo.ps1 (psutils)
# to debug sudo problems.
# to download:
# (new-object net.webclient).downloadfile('https://gist.githubusercontent.com/lukesampson/9130445/raw/sudo_debug.ps1', "$pwd/sudo_debug.ps1")
# to test:
# ./sudo_debug.ps1 echo hi
if(!$args) { "usage: sudo <cmd...>"; exit 1 }
function is_admin {
write-host "DEBUG: is_admin"
$id = [security.principal.windowsidentity]::getcurrent()
write-host "DEBUG: is_admin: identity: $($id.name)"
$name = $id.name -replace '^[^\\]*\\', ''
$admin_group = (gwmi win32_group -filter "LocalAccount=True AND SID='S-1-5-32-544'").name # be language-agnostic
write-host "DEBUG: is_admin: admin group: $admin_group"
$res = gwmi win32_groupuser | ? { $_.groupcomponent -match "name=`"$admin_group`"" -and $_.partcomponent -match "name=`"$name`"" }
write-host "DEBUG: is_admin: gwmi result: $res"
if($res) { $true }
}
function sudo_do($parent_pid, $dir, $cmd) {
write-host "DEBUG: sudo_do"
$src = 'using System.Runtime.InteropServices;
public class Kernel {
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
public static extern bool FreeConsole();
}'
write-host "DEBUG: sudo_do: adding Kernel functions"
$kernel = add-type $src -passthru
write-host "DEBUG: sudo_do: Kernel functions were added"
write-host "DEBUG: freeing console"
$kernel::freeconsole()
write-host "DEBUG: sudo_do: console freed"
write-host "DEBUG: sudo_do: attaching to parent: pid = $parent_pid"
$kernel::attachconsole($parent_pid)
$p = new-object diagnostics.process; $start = $p.startinfo
$start.filename = "powershell.exe"
$start.arguments = "-noprofile $cmd`nexit `$lastexitcode"
write-host "DEBUG: sudo_do: powershell arguments: '$($start.arguments)'"
$start.useshellexecute = $false
$start.workingdirectory = $dir
write-host "DEBUG: sudo_do: starting process"
$p.start()
write-host "DEBUG: sudo_do: waiting for process to exit"
$p.waitforexit()
write-host "DEBUG: sudo_do: process exited (exit code = $($p.exitcode)"
return $p.exitcode
}
function serialize($a, $escape) {
if($a -is [string] -and $a -match '\s') { return "'$a'" }
if($a -is [array]) {
return $a | % { (serialize $_ $escape) -join ', ' }
}
if($escape) { return $a -replace '[>&]', '`$0' }
return $a
}
if($args[0] -eq '-do') {
write-host "DEBUG: child process"
$null, $dir, $parent_pid, $cmd = $args
$exit_code = sudo_do $parent_pid $dir (serialize $cmd)
write-host "DEBUG: child process: exit code: $exit_code"
exit $exit_code
}
if(!(is_admin)) {
[console]::error.writeline("sudo: you must be an administrator to run sudo")
exit 1
}
$a = serialize $args $true
write-host "DEBUG: args: $a"
$savetitle = $host.ui.rawui.windowtitle
$p = new-object diagnostics.process; $start = $p.startinfo
$start.filename = "powershell.exe"
$wd = convert-path $pwd # convert in case pwd is a PSDrive
$start.arguments = "-noprofile & '$pscommandpath' -do $wd $pid $a`nexit `$lastexitcode"
$start.verb = 'runas'
# don't hide window for debug
# $start.windowstyle = 'hidden'
try {
write-host "DEBUG: starting powershell process"
$null = $p.start()
write-host "DEBUG: successfully started powershell process"
} catch { exit 1 } # user didn't provide consent
write-host "DEBUG: waiting for powershell process to exit"
$p.waitforexit()
write-host "DEBUG: powershell process exited. exit code $($p.exitcode)"
$host.ui.rawui.windowtitle = $savetitle
exit $p.exitcode
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment