Recuperando senhas esquecidas do RDCMan com PowerShell

Esqueci a senha de um usuário padrão em um servidor interno. Felizmente, eu havia me conectado a este servidor anteriormente usando o RDCMan (Remote Desktop Connection Manager) em outro computador e salvado a senha. Após algum esforço, consegui recuperar a senha.

O script PowerShell que funcionou finalmente foi encontrado no seguinte endereço:

https://www.undocumented-features.com/2019/10/03/decrypting-credentials-stored-in-remote-desktop-manager-rdcman-rdg/

A descoberta deste link foi possível graças a um artigo no blog "The Garden" (cnblogs) que apresentou três soluções, incluindo uma com PowerShell.

https://www.cnblogs.com/Thorndike/p/15325079.html

Como a primeira tentaitva não funcionou, procurei por palavras-chave dentro do script e encontrei a solução (pesquisas no Baidu e Bing não produziram resultados úteis; desta vez, o Google foi o moter de busca eficaz).

O script final e funcional é:


param($RDGFilePath,
    $EncryptedPassword,
    $RDCManExecutablePath
    )

# Se o caminho do executável do RDCMan não for fornecido, tenta encontrá-lo
if (-not $RDCManExecutablePath) {
    $RDCManExecutablePath = (Get-ChildItem -Path @('C:\Program Files\Microsoft', 'C:\Program Files (x86)\Microsoft') -Filter "RDCMan.exe" -Recurse -ErrorAction SilentlyContinue)[0]
}

# Verifica se o RDCMan foi encontrado
if (-not $RDCManExecutablePath) {
    Write-Error "O Remote Desktop Connection Manager (RDCMan) deve estar instalado. Se estiver instalado, use o parâmetro -RDCManExecutablePath para especificar o local do executável."
    Exit
} else {
    Write-Host "Carregando o assembly do RDCMan em: $($RDCManExecutablePath.FullName)"
    try {
        # Carrega o assembly do RDCMan
        $Assembly = [Reflection.Assembly]::LoadFile($RDCManExecutablePath.FullName)
    } catch {
        Write-Host "Erro ao carregar o assembly: $($_.Exception.Message)"
        Exit
    }
    try {
        Import-Module $Assembly
    } catch {
        Write-Host "Erro ao importar o módulo: $($_.Exception.Message)"
        exit
    }
}

# Se um arquivo .rdg for fornecido, descriptografa as credenciais contidas nele
if ($RDGFilePath) {
    Write-Host "Processando o arquivo RDG: $RDGFilePath"
    [xml]$XmlContent = Get-Content $RDGFilePath
    $CredentialNodes = $XmlContent.SelectNodes("//logonCredentials")
    $DecryptedCredentials = @()

    foreach ($node in $CredentialNodes) {
        try {
            # Cria uma instância das configurações de criptografia
            $EncryptionSettings = New-Object -TypeName RdcMan.EncryptionSettings
            # Descriptografa a senha
            $Password = [RdcMan.Encryption]::DecryptString($node.password, $EncryptionSettings)
        } catch {
            Write-Host "Falha ao descriptografar uma senha: $($_.Exception.Message)"
            continue # Pula para o próximo nó em caso de erro
        }

        # Adiciona as credenciais descriptografadas à lista, se a descriptografia foi bem-sucedida
        if ($Password -and ($Password -notlike '*Failed to decrypt*')) {
            $CredentialObject = New-Object PSObject
            $CredentialObject | Add-Member -Type NoteProperty -Name "ProfileName" -Value $node.ProfileName -Force
            $CredentialObject | Add-Member -Type NoteProperty -Name "UserName" -Value $node.username -Force
            $CredentialObject | Add-Member -Type NoteProperty -Name "Password" -Value $Password -Force
            $CredentialObject | Add-Member -Type NoteProperty -Name "Domain" -Value $node.domain -Force
            $DecryptedCredentials += $CredentialObject
        }
    }

    # Exibe as credenciais descriptografadas
    if ($DecryptedCredentials) {
        $DecryptedCredentials | Format-Table -AutoSize
    } else {
        Write-Host "Nenhuma credencial descriptografada encontrada no arquivo."
    }
} elseif ($EncryptedPassword) {
    # Se apenas uma senha criptografada for fornecida, descriptografa-a diretamente
    $EncryptionSettings = New-Object -TypeName RdcMan.EncryptionSettings
    $Password = [RdcMan.Encryption]::DecryptString($EncryptedPassword, $EncryptionSettings)
    Write-Host "Senha descriptografada: $Password"
}

No meu caso, usei uma versão portátil do RDCMan, então precisei passar o caminho do executável como parâmetro:


.\MeuScriptDescriptografador.ps1 -RDGFilePath '.\MeuArquivo.rdg' -RDCManExecutablePath 'D:\PortableApps\RDCMan\RDCMan.exe'

Outra tentativa de script que fiz foi:


function Decrypt-RDCManCredentials ($FilePath) {
    <#
    .SYNOPSIS
    Este script tenta descriptografar todas as senhas armazenadas em um arquivo de configuração do RDCMan.

    .EXAMPLE
    Decrypt-RDCManCredentials -FilePath "C:\Caminho\Para\SeuArquivo.rdg"
    #>

    if (-not $FilePath) {
        # Se nenhum arquivo for especificado, tenta usar o arquivo de configurações padrão
        $DefaultConfigPath = "$env:LOCALAPPDATA\microsoft\remote desktop connection manager\rdcman.settings"
        if (Test-Path $DefaultConfigPath) {
            [xml]$Config = Get-Content $DefaultConfigPath
            $FilesToOpen = Select-Xml -Xml $Config -XPath "//FilesToOpen/*"
            $FilesToOpen | ForEach-Object {
                Write-Host "Processando arquivo: $($_.Node.InnerText)"
                Decrypt-RDCManCredentials -FilePath $_.Node.InnerText
            }
        } else {
            Write-Host "Arquivo de configurações padrão não encontrado. Por favor, especifique o caminho do arquivo .rdg."
        }
    } else {
        Write-Host "Lendo o arquivo: $FilePath"
        $FileContent = Get-Content $FilePath
        [xml]$XmlData = $FileContent

        # Tenta selecionar os nós de credenciais
        $CredentialNodes = Select-Xml -Xml $XmlData -XPath "//logonCredentials"

        # O caminho para a senha pode variar dependendo da versão do RDCMan
        $CredentialNodes | ForEach-Object {
            $Node = $_.Node
            $EncryptedPassword = $null
            $PasswordText = $null

            # Tenta obter a senha do atributo 'password'
            if ($Node.Password) {
                $EncryptedPassword = $Node.Password
            }
            # Em algumas versões, a senha pode estar no texto do nó, dentro de um elemento específico
            elseif ($Node.SelectSingleNode(".//password") -and $Node.SelectSingleNode(".//password").#text) {
                 $EncryptedPassword = $Node.SelectSingleNode(".//password").#text
            }

            if ($EncryptedPassword) {
                $PasswordText = Decrypt-DPAPI $EncryptedPassword
                $Output = "$($Node.Domain)\$($Node.Username) - Senha: $PasswordText"
                if ($EncryptedPassword) { $Output += " (Hash: $EncryptedPassword)" }
                Write-Output $Output
            } else {
                Write-Host "Não foi possível encontrar a senha para $($Node.Domain)\$($Node.Username)"
            }
        }
    }
}

function Decrypt-DPAPI ($EncryptedString) {
    # Carrega o assembly System.Security no runspace do PowerShell
    Add-Type -assembly System.Security
    $AsciiEncoding = [System.Text.Encoding]::ASCII
    $UnicodeEncoding = [System.Text.Encoding]::UNICODE

    # Tenta descriptografar a senha usando o escopo CurrentUser
    try {
        $EncryptedBytes = [System.Convert]::FromBase64String($EncryptedString)
        $DecryptedBytes = [System.Security.Cryptography.ProtectedData]::Unprotect($EncryptedBytes, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)
        $DecodedPassword = $UnicodeEncoding.GetString($DecryptedBytes)
        return $DecodedPassword
    }
    catch {
        # Se falhar com CurrentUser, tenta com LocalMachine
        try {
            $EncryptedBytes = [System.Convert]::FromBase64String($EncryptedString)
            $DecryptedBytes = [System.Security.Cryptography.ProtectedData]::Unprotect($EncryptedBytes, $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine)
            $DecodedPassword = $UnicodeEncoding.GetString($DecryptedBytes)
            return $DecodedPassword
        }
        catch {
            # Retorna uma mensagem de erro se ambas as tentativas falharem
            return "Falha ao descriptografar"
        }
    }
}

O script do Thorndkie (do cnblogs), com correções manuais após reconhecimento de texto:


# Copia o executável do RDCMan para um local temporário como DLL
Copy-Item 'C:\Program Files (x86)\Microsoft\Remote Desktop Connection Manager\RDCMan.exe' 'C:\windows\temp\RDCMan.dll'

# Importa o módulo a partir do arquivo copiado
Import-Module 'C:\windows\temp\RDCMan.dll'

# Cria uma instância das configurações de criptografia
$EncryptionSettings = New-Object -TypeName RdcMan.EncryptionSettings

# Lê as senhas criptografadas de um arquivo (uma por linha)
$EncryptedLines = Get-Content RDCManpass.txt

# Descriptografa cada linha
foreach ($line in $EncryptedLines) {
    $EncryptedPassword = $line
    [RdcMan.Encryption]::DecryptString($EncryptedPassword, $EncryptionSettings)
}

Executar o script diretamente no Windows 10 pode gerar erros. A solução encontrada foi:

https://blog.csdn.net/qq_15585305/article/details/131436046

Para passagem de parâmetros em scripts PowerShell, consultei este artigo:

https://blog.csdn.net/wan_ghuan/article/details/104346908

Tags: PowerShell RDCMan criptografia descriptografia gerenciamento de senhas

Publicado em 7-19 06:46