param( [Parameter(Mandatory=$true)][string]$AssessmentId, [Parameter(Mandatory=$true)][string]$UploadToken, [string]$ApiBase = 'https://network.gortnet.de', [string]$OutputPath = (Join-Path $env:TEMP 'GORTNET-NetworkAssessment-result.json') ) Set-StrictMode -Version 2.0 $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' $checks = @() function Add-Check { param([string]$Id,[string]$Category,[string]$Status,[string]$Title,[string]$Detail) $script:checks += [pscustomobject]@{id=$Id;category=$Category;status=$Status;title=$Title;detail=$Detail} } function Test-TcpPort { param([string]$HostName,[int]$Port,[int]$TimeoutMs=1500) $c = New-Object Net.Sockets.TcpClient try { $a = $c.BeginConnect($HostName,$Port,$null,$null) if(-not $a.AsyncWaitHandle.WaitOne($TimeoutMs,$false)){ return $false } $c.EndConnect($a) return $c.Connected } catch { return $false } finally { $c.Close() } } $cs = Get-CimInstance Win32_ComputerSystem $os = Get-CimInstance Win32_OperatingSystem $joined = [bool]$cs.PartOfDomain $domain = if($joined){[string]$cs.Domain}else{$null} $dnsServers = @(Get-DnsClientServerAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object {$_.ServerAddresses} | ForEach-Object {$_.ServerAddresses} | Sort-Object -Unique) if($joined){ Add-Check 'domain-join' 'AD Discovery' 'pass' 'Domain Join Status' 'Computer is joined to an Active Directory domain.' }else{ Add-Check 'domain-join' 'AD Discovery' 'warning' 'Domain Join Status' 'Computer is not joined to an Active Directory domain.' } $dcs = @() if($joined -and $domain){ try{ $srv = Resolve-DnsName -Type SRV "_ldap._tcp.dc._msdcs.$domain" -ErrorAction Stop $dcs = @($srv | Where-Object {$_.Type -eq 'SRV'} | ForEach-Object {$_.NameTarget.TrimEnd('.')} | Sort-Object -Unique) if($dcs.Count -gt 0){ Add-Check 'dns-ldap-srv' 'DNS' 'pass' 'AD LDAP SRV Records' "$($dcs.Count) domain controller target(s) resolved." }else{ Add-Check 'dns-ldap-srv' 'DNS' 'fail' 'AD LDAP SRV Records' 'No domain controller SRV target returned.' } }catch{ Add-Check 'dns-ldap-srv' 'DNS' 'fail' 'AD LDAP SRV Records' 'AD LDAP SRV lookup failed.' } try{ $k = Resolve-DnsName -Type SRV "_kerberos._tcp.$domain" -ErrorAction Stop if($k){ Add-Check 'dns-kerberos-srv' 'DNS' 'pass' 'Kerberos SRV Records' 'Kerberos SRV records resolved.' } }catch{ Add-Check 'dns-kerberos-srv' 'DNS' 'fail' 'Kerberos SRV Records' 'Kerberos SRV lookup failed.' } } if($dnsServers.Count -gt 0){ Add-Check 'dns-servers' 'DNS' 'pass' 'DNS Servers Configured' "$($dnsServers.Count) IPv4 DNS server(s) detected." }else{ Add-Check 'dns-servers' 'DNS' 'fail' 'DNS Servers Configured' 'No IPv4 DNS servers detected.' } $gateway = Get-NetRoute -DestinationPrefix '0.0.0.0/0' -ErrorAction SilentlyContinue | Sort-Object RouteMetric,InterfaceMetric | Select-Object -First 1 if($gateway){ Add-Check 'default-gateway' 'Network' 'pass' 'Default Gateway' 'An IPv4 default gateway is configured.' }else{ Add-Check 'default-gateway' 'Network' 'warning' 'Default Gateway' 'No IPv4 default gateway detected.' } $portMap = @( @{Port=53;Category='DNS';Name='DNS TCP 53'}, @{Port=88;Category='Kerberos';Name='Kerberos TCP 88'}, @{Port=135;Category='Network';Name='RPC Endpoint Mapper 135'}, @{Port=389;Category='LDAP/LDAPS';Name='LDAP 389'}, @{Port=445;Category='SMB';Name='SMB 445'}, @{Port=464;Category='Kerberos';Name='Kerberos Password 464'}, @{Port=636;Category='LDAP/LDAPS';Name='LDAPS 636'}, @{Port=3268;Category='LDAP/LDAPS';Name='Global Catalog 3268'}, @{Port=3269;Category='LDAP/LDAPS';Name='Global Catalog TLS 3269'} ) for($i=0;$i -lt $dcs.Count;$i++){ $dc = $dcs[$i] $tag = 'dc' + ($i+1) foreach($item in $portMap){ $ok = Test-TcpPort $dc $item.Port Add-Check ("tcp-$($item.Port)-$tag") $item.Category $(if($ok){'pass'}else{'fail'}) $item.Name $(if($ok){"TCP/$($item.Port) reachable on discovered domain controller."}else{"TCP/$($item.Port) not reachable on discovered domain controller."}) } } if($joined -and $domain){ try{ & nltest.exe "/sc_verify:$domain" *> $null if($LASTEXITCODE -eq 0){ Add-Check 'secure-channel' 'Secure Channel' 'pass' 'Computer Secure Channel' 'Netlogon secure channel verification succeeded.' }else{ Add-Check 'secure-channel' 'Secure Channel' 'fail' 'Computer Secure Channel' 'Netlogon secure channel verification failed.' } }catch{ Add-Check 'secure-channel' 'Secure Channel' 'warning' 'Computer Secure Channel' 'Secure channel could not be verified.' } } try{ & w32tm.exe /query /source *> $null if($LASTEXITCODE -eq 0){ Add-Check 'time-source' 'Time' 'pass' 'Windows Time Source' 'Windows Time is configured and responding.' } }catch{ Add-Check 'time-source' 'Time' 'warning' 'Windows Time Source' 'Windows Time status could not be queried.' } try{ $kl = & klist.exe 2>$null if(($kl -join [Environment]::NewLine) -match 'krbtgt/'){ Add-Check 'kerberos-ticket-cache' 'Kerberos' 'pass' 'Kerberos Ticket Cache' 'A Kerberos TGT is present in the current logon session.' }else{ Add-Check 'kerberos-ticket-cache' 'Kerberos' 'warning' 'Kerberos Ticket Cache' 'No Kerberos TGT detected in the current logon session.' } }catch{} $payload = [ordered]@{ schemaVersion = 1 environment = [ordered]@{ domain = $null domainJoined = $joined activeDirectorySite = $null os = "$($os.Caption) $($os.Version)" dnsServers = @() domainControllers = @() } checks = @($checks) } $json = $payload | ConvertTo-Json -Depth 8 [IO.File]::WriteAllText($OutputPath,$json,[Text.UTF8Encoding]::new($false)) Write-Host "GORTNET Network Assessment Probe 0.2.0" Write-Host "Domain names, DC names, DNS IPs and gateway values are not uploaded." Write-Host ("Checks: " + $checks.Count) $uri = "$($ApiBase.TrimEnd('/'))/v1/assessments/$AssessmentId/results" $response = Invoke-RestMethod -Method Post -Uri $uri -Headers @{Authorization="Bearer $UploadToken"} -ContentType 'application/json' -Body $json -TimeoutSec 30 Write-Host ("Upload status: " + $response.status) Write-Host ("Server score: " + $response.score)