Notice: Due to size constraints and loading performance considerations, scripts referenced in blog posts are not attached directly. To request access, please complete the following form: Script Request Form Note: A Google account is required to access the form.
Disclaimer: I do not accept responsibility for any issues arising from scripts being run without adequate understanding. It is the user's responsibility to review and assess any code before execution. More information

Finding Server Hostnames from IP Addresses: The RDP Certificate Method

Have you ever found yourself in a situation where you need to connect to a Windows server by IP address, but you're not sure what the actual hostname is? Maybe DNS records are outdated, or reverse DNS lookups aren't configured properly. I've been there many times, and it's frustrating when you're trying to manage servers and can't easily identify them.

Recently, I discovered a clever solution that utilises a server where RDP enabled, as this protocols  typically uses a certificate for secure connections. This certificate often contains the server's actual hostname in the Common Name (CN) field. By extracting this certificate information, we can identify the server without relying on DNS.

The Problem: When DNS does not resolve?

In enterprise environments, it's common to encounter scenarios where:

  • DNS records haven't been updated after server migrations
  • Reverse DNS zones aren't properly configured
  • You're working with IP addresses from network scans
  • Legacy systems have inconsistent naming conventions

I've spent countless hours trying to figure out which server is which, especially when dealing with virtualized environments where servers might have been cloned or migrated.

Visual Output from the script 

This will is an image of the results from the script when it is run:

RDP Session Certificate Extraction

The idea is simple but effective. When you connect to an RDP server, it presents a certificate for the secure connection. This certificate usually contains the server's hostname in the subject field. By programmatically extracting this certificate, we can discover the actual server name.

Here's how I approach this problem with PowerShell:

# Basic connection to extract SSL certificate
$sslStream = New-Object System.Net.Security.SslStream($networkStream, $false, {
    param($sender, $certificate, $chain, $sslPolicyErrors)
    return $true  # Accept any certificate for extraction
})

$sslStream.AuthenticateAsClient($ServerName)
$certificate = $sslStream.RemoteCertificate

The key insight is that we're not trying to validate the certificate - we're just extracting information from it. This means it works even with self-signed certificates or certificates with validation errors.

The Powershell Lookup Tool

I've developed a comprehensive PowerShell script that handles multiple scenarios and fallback methods. The script tries different approaches to ensure maximum compatibility:

Primary Method: Direct SSL Connection

function Get-RDPCertificate {
    param([string]$ServerName, [int]$ServerPort, [int]$Timeout)
    
    try {
        $tcpClient = New-Object System.Net.Sockets.TcpClient
        $tcpClient.Connect($ServerName, $ServerPort)
        $networkStream = $tcpClient.GetStream()
        
        $sslStream = New-Object System.Net.Security.SslStream(
            $networkStream, 
            $false, 
            { return $true }  # Accept any certificate
        )
        
        $sslStream.AuthenticateAsClient($ServerName)
        $certificate = $sslStream.RemoteCertificate
        
        if ($certificate) {
            $cert2 = New-Object System.Security.Cryptography.X509Certificates.
            X509Certificate2($certificate)
            
            # Extract hostname from certificate subject
            if ($cert2.Subject -match "CN=([^,]+)") {
                $commonName = $matches[1].Trim()
                Write-Host "Server Hostname: $commonName" -ForegroundColor Cyan
                return $commonName
            }
        }
    }
    catch {
        # Fallback to alternative methods
    }
}

Fallback Method: OpenSSL Integration

For servers that don't respond to standard SSL connections, I use OpenSSL as a fallback:

function Get-RDPCertificateViaOpenSSL {
    param([string]$ServerName, [int]$ServerPort)
    
    $opensslCmd = "echo | openssl s_client -connect ${ServerName}:${ServerPort} 
    -servername $ServerName 2>nul"
    $result = Invoke-Expression $opensslCmd
    
    # Parse certificate from OpenSSL output and extract CN
}

Setting Up Reverse DNS in Active Directory

While the certificate method works great, it's also worth setting up proper reverse DNS zones in Active Directory. This ensures that standard DNS lookups work correctly across your environment.

Creating Reverse DNS Zones

# Create reverse DNS zone for 192.168.1.0/24 network
Add-DnsServerPrimaryZone -NetworkId "192.168.1.0/24" -ReplicationScope "Forest"

# Add PTR records for servers
Add-DnsServerResourceRecordPtr -ZoneName "1.168.192.in-addr.arpa" -Name "100" 
-ComputerName "SERVER01.domain.local"
Add-DnsServerResourceRecordPtr -ZoneName "1.168.192.in-addr.arpa" -Name "101" 
-ComputerName "SERVER02.domain.local"

Automating PTR Record Creation

This script to automatically create PTR records from the A record that is found, this should only really be required for larger environments:

# Get all A records from forward zones
$ForwardZones = Get-DnsServerZone | Where-Object {$_.IsReverseLookupZone -eq $false}

foreach ($Zone in $ForwardZones) {
    $ARecords = Get-DnsServerResourceRecord -ZoneName $Zone.ZoneName -RRType A
    
    foreach ($Record in $ARecords) {
        $IP = $Record.RecordData.IPv4Address.ToString()
        $FQDN = $Record.HostName + "." + $Zone.ZoneName
        
        # Create corresponding PTR record
        Add-DnsServerResourceRecordPtr -ZoneName $ReverseZone -Name $LastOctet 
        -ComputerName $FQDN
    }
}
You can run the script like this:
.\Get-RDPCertificate.ps1 -Server "<ip address of server>" -TimeoutSeconds 15

Script : Get-RDPCertificate.ps1

param(
    [Parameter(Mandatory=$true)]
    [string]$Server,
    
    [Parameter(Mandatory=$false)]
    [int]$Port = 3389,
    
    [Parameter(Mandatory=$false)]
    [int]$TimeoutSeconds = 10
)

function Get-RDPCertificate {
    param(
        [string]$ServerName,
        [int]$ServerPort,
        [int]$Timeout
    )
    
    try {
        Write-Host "Connecting to $ServerName on port $ServerPort..." -ForegroundColor Yellow
        
        # Create TCP client
        $tcpClient = New-Object System.Net.Sockets.TcpClient
        $tcpClient.ReceiveTimeout = $Timeout * 1000
        $tcpClient.SendTimeout = $Timeout * 1000
        
        # Connect to server
        $tcpClient.Connect($ServerName, $ServerPort)
        $networkStream = $tcpClient.GetStream()
        
        # RDP uses TLS/SSL for certificate exchange
        # We'll use SslStream to get the certificate
        $sslStream = New-Object System.Net.Security.SslStream(
            $networkStream, 
            $false, 
            { param($sender, $certificate, $chain, $sslPolicyErrors) return $true }
        )
        
        # Authenticate and get certificate
        $sslStream.AuthenticateAsClient($ServerName)
        $certificate = $sslStream.RemoteCertificate
        
        if ($certificate) {
            $cert2 = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certificate)
            
            Write-Host "`nRDP Certificate Found!" -ForegroundColor Green
            Write-Host "=====================" -ForegroundColor Green
            
            # Extract hostname from certificate subject
            $subject = $cert2.Subject
            $commonName = ""
            
            if ($subject -match "CN=([^,]+)") {
                $commonName = $matches[1].Trim()
                Write-Host "Server Hostname: $commonName" -ForegroundColor Cyan
            }
            
            Write-Host "Certificate Subject: $subject" -ForegroundColor White
            Write-Host "Certificate Issuer: $($cert2.Issuer)" -ForegroundColor White
            Write-Host "Valid From: $($cert2.NotBefore)" -ForegroundColor White
            Write-Host "Valid To: $($cert2.NotAfter)" -ForegroundColor White
            
            # Check for Subject Alternative Names
            foreach ($extension in $cert2.Extensions) {
                if ($extension.Oid.FriendlyName -eq "Subject Alternative Name") {
                    Write-Host "Alternative Names: $($extension.Format($false))" -ForegroundColor White
                    break
                }
            }
            
            $sslStream.Close()
            $tcpClient.Close()
            
            return $commonName
        }
        
    } catch [System.Security.Authentication.AuthenticationException] {
        # This is expected - RDP might not use standard SSL/TLS
        Write-Host "Standard SSL failed, trying RDP-specific method..." -ForegroundColor Yellow
        return Get-RDPCertificateViaOpenSSL -ServerName $ServerName -ServerPort $ServerPort
        
    } catch {
        Write-Host "Connection failed: $($_.Exception.Message)" -ForegroundColor Red
        return $null
    } finally {
        if ($sslStream) { $sslStream.Close() }
        if ($tcpClient) { $tcpClient.Close() }
    }
}

function Get-RDPCertificateViaOpenSSL {
    param(
        [string]$ServerName,
        [int]$ServerPort
    )
    
    try {
        # Use openssl command if available (common on Windows with Git or WSL)
        $opensslPath = Get-Command openssl -ErrorAction SilentlyContinue
        
        if ($opensslPath) {
            Write-Host "Using OpenSSL to extract RDP certificate..." -ForegroundColor Yellow
            
            $opensslCmd = "echo | openssl s_client -connect ${ServerName}:${ServerPort} -servername $ServerName 2>nul"
            $result = Invoke-Expression $opensslCmd
            
            # Parse certificate from OpenSSL output
            $certStart = $false
            $certLines = @()
            
            foreach ($line in $result) {
                if ($line -match "-----BEGIN CERTIFICATE-----") {
                    $certStart = $true
                }
                if ($certStart) {
                    $certLines += $line
                }
                if ($line -match "-----END CERTIFICATE-----") {
                    break
                }
            }
            
            if ($certLines.Count -gt 0) {
                # Convert certificate text to X509Certificate2
                $certText = $certLines -join "`n"
                $certBytes = [System.Convert]::FromBase64String(($certText -replace "-----BEGIN CERTIFICATE-----" -replace "-----END CERTIFICATE-----" -replace "`n" -replace "`r"))
                $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certBytes)
                
                Write-Host "`nRDP Certificate Extracted!" -ForegroundColor Green
                Write-Host "==========================" -ForegroundColor Green
                
                $subject = $cert.Subject
                if ($subject -match "CN=([^,]+)") {
                    $commonName = $matches[1].Trim()
                    Write-Host "Server Hostname: $commonName" -ForegroundColor Cyan
                    return $commonName
                }
            }
        }
        
        # Fallback: Try using .NET socket directly with RDP protocol
        return Get-RDPHostnameViaNative -ServerName $ServerName -ServerPort $ServerPort
        
    } catch {
        Write-Host "OpenSSL method failed: $($_.Exception.Message)" -ForegroundColor Red
        return Get-RDPHostnameViaNative -ServerName $ServerName -ServerPort $ServerPort
    }
}

function Get-RDPHostnameViaNative {
    param(
        [string]$ServerName,
        [int]$ServerPort
    )
    
    Write-Host "Attempting direct RDP certificate extraction..." -ForegroundColor Yellow
    
    try {
        # Create socket connection
        $tcpClient = New-Object System.Net.Sockets.TcpClient
        $tcpClient.Connect($ServerName, $ServerPort)
        $stream = $tcpClient.GetStream()
        
        # RDP Connection Request (X.224)
        $x224Request = [byte[]](
            0x03, 0x00, 0x00, 0x13,  # TPKT Header
            0x0E, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,  # X.224 Connection Request
            0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00  # RDP Negotiation Request
        )
        
        $stream.Write($x224Request, 0, $x224Request.Length)
        
        # Read response
        $buffer = New-Object byte[] 1024
        $bytesRead = $stream.Read($buffer, 0, $buffer.Length)
        
        if ($bytesRead -gt 0) {
            Write-Host "RDP handshake successful" -ForegroundColor Green
            Write-Host "Note: Full certificate extraction from RDP requires TLS negotiation" -ForegroundColor Yellow
            Write-Host "Server is responding on RDP port $ServerPort" -ForegroundColor Green
            
            # For now, return the server name as provided
            # In a full implementation, you'd continue the RDP handshake to get to TLS
            return "RDP-Host-$ServerName"
        }
        
        $stream.Close()
        $tcpClient.Close()
        
    } catch {
        Write-Host "Native RDP method failed: $($_.Exception.Message)" -ForegroundColor Red
        return $null
    }
    
    return $null
}

# Main execution
Write-Host "RDP Certificate Extractor" -ForegroundColor Magenta
Write-Host "=========================" -ForegroundColor Magenta

$result = Get-RDPCertificate -ServerName $Server -ServerPort $Port -Timeout $TimeoutSeconds

if ($result) {
    Write-Host "`nExtracted Computer Name: $result" -ForegroundColor Green -BackgroundColor Black
} else {
    Write-Host "`nUnable to extract certificate information" -ForegroundColor Red
}

Write-Host "`nScript completed." -ForegroundColor Magenta

Conclusion

This approach has saved me countless hours of detective work when dealing with servers that can't be easily identified through DNS. The certificate method is reliable, works with various server configurations, and provides additional useful information about the server's certificate status.

Combined with proper reverse DNS setup in Active Directory, you'll have a comprehensive solution for server identification in your environment. The PowerShell script handles multiple scenarios and provides fallback methods.

Previous Post Next Post

نموذج الاتصال