Files
inquiry_robot/deploy/_remote_mysql_cred_probe.ps1
T

146 lines
6.8 KiB
PowerShell

# Extract password candidates from application-database-credentials.json + try local root / create DB.
$ErrorActionPreference = "Continue"
$mysql = "C:\yutongda\tools\mysql-8.0.39-winx64\bin\mysql.exe"
$credPath = "C:\yutongda\secure\application-database-credentials.json"
Write-Output "=== application-database-credentials.json keys (no values) ==="
$j = Get-Content $credPath -Raw | ConvertFrom-Json
function Show-Keys($obj, $prefix) {
foreach ($p in $obj.PSObject.Properties) {
$name = if ($prefix) { "$prefix.$($p.Name)" } else { $p.Name }
if ($null -eq $p.Value) { Write-Output (" $name = null"); continue }
if ($p.Value -is [string]) {
Write-Output (" $name string len=" + $p.Value.Length)
} elseif ($p.Value -is [ValueType]) {
Write-Output (" $name = " + $p.Value)
} elseif ($p.Value -is [System.Collections.IEnumerable] -and -not ($p.Value -is [string])) {
$i = 0
foreach ($item in $p.Value) {
if ($item -is [psobject]) { Show-Keys $item ("$name[$i]") }
else { Write-Output (" $name[$i] type=" + $item.GetType().Name) }
$i++
}
} elseif ($p.Value -is [psobject]) {
Show-Keys $p.Value $name
} else {
Write-Output (" $name type=" + $p.Value.GetType().Name)
}
}
}
Show-Keys $j ""
# Collect all string values that look like passwords
$cands = New-Object System.Collections.Generic.List[string]
function Collect-Strings($obj) {
foreach ($p in $obj.PSObject.Properties) {
if ($p.Value -is [string] -and $p.Value.Length -ge 4 -and $p.Value.Length -le 128) {
if ($p.Name -match '(?i)pass|secret|pwd|root|token') { $cands.Add($p.Value) }
elseif ($p.Value -match '[^a-zA-Z0-9./:@_-]' -or $p.Value.Length -ge 12) {
# heuristic: long or special-char strings as candidates
if ($p.Name -match '(?i)pass|secret|pwd|credential') { $cands.Add($p.Value) }
}
} elseif ($p.Value -is [psobject]) { Collect-Strings $p.Value }
elseif ($p.Value -is [System.Collections.IEnumerable] -and -not ($p.Value -is [string])) {
foreach ($item in $p.Value) { if ($item -is [psobject]) { Collect-Strings $item } }
}
}
}
Collect-Strings $j
# Also MYSQL_PASSWORD from .env and secrets json
$envText = Get-Content "E:\wwwroot\.env" -Raw
if ($envText -match '(?m)^MYSQL_PASSWORD=(.+)$') { $cands.Add($Matches[1].Trim()) }
$sec = Get-Content "E:\wwwroot\ops\inquiry_robot_db_secrets.json" -Raw | ConvertFrom-Json
if ($sec.mysql_password) { $cands.Add([string]$sec.mysql_password) }
# Try as root
Write-Output ("cand_count=" + $cands.Count)
$hitRoot = $false
$seen = @{}
foreach ($p in $cands) {
if ($seen.ContainsKey($p)) { continue }
$seen[$p] = $true
$out = & $mysql -uroot "-p$p" -e "SELECT 'root_ok' AS r" 2>&1 | Out-String
if ($out -match "root_ok") {
Write-Output ("ROOT_HIT len=" + $p.Length)
Set-Content -Path "E:\wwwroot\ops\.mysql_root_stash.tmp" -Value $p -Encoding ascii -NoNewline
$hitRoot = $true
break
}
}
if (-not $hitRoot) { Write-Output "ROOT_NO_HIT" }
# Try each username/password pair from credentials against local mysql for CREATE privilege
Write-Output "=== try app users on local 127.0.0.1 ==="
function Try-User($user, $pass) {
if ([string]::IsNullOrWhiteSpace($user) -or [string]::IsNullOrWhiteSpace($pass)) { return }
$sqlFile = [System.IO.Path]::GetTempFileName() + ".sql"
Set-Content -Path $sqlFile -Value "SELECT CURRENT_USER() AS u; SHOW GRANTS;" -Encoding ascii
$out = & $mysql -h127.0.0.1 "-u$user" "-p$pass" --batch --raw -e "source $sqlFile" 2>&1 | Out-String
if ($out -notmatch "ERROR" -and $out -match "Grants for|CURRENT_USER|@") {
Write-Output ("USER_OK user=" + $user + " passLen=" + $pass.Length)
($out -split "`r?`n" | Where-Object { $_ -match "GRANT|CURRENT_USER|@" } | Select-Object -First 12) | ForEach-Object { " " + $_ }
} else {
# retry simpler one-liner without source
$out2 = & $mysql -h127.0.0.1 "-u$user" "-p$pass" -e "SELECT 1 AS ok" 2>&1 | Out-String
if ($out2 -match "(?m)^1\s*$|ok") {
Write-Output ("USER_OK_SIMPLE user=" + $user + " passLen=" + $pass.Length)
$g = & $mysql -h127.0.0.1 "-u$user" "-p$pass" -e "SHOW GRANTS" 2>&1 | Out-String
($g -split "`r?`n" | Where-Object { $_ -match "GRANT" } | Select-Object -First 8) | ForEach-Object { " " + $_ }
} else {
$err = ($out2 -split "`r?`n" | Where-Object { $_ -match "ERROR" } | Select-Object -First 1)
Write-Output ("USER_FAIL user=" + $user + " " + $err)
}
}
Remove-Item $sqlFile -ErrorAction SilentlyContinue
}
# Walk JSON for user/password pairs heuristically
function Walk-Pairs($obj, $ctxUser) {
$user = $ctxUser
$pass = $null
foreach ($p in $obj.PSObject.Properties) {
if ($p.Name -match '(?i)^(username|user|mysql_user|db_user)$' -and $p.Value -is [string]) { $user = $p.Value }
if ($p.Name -match '(?i)^(password|pass|mysql_password|db_password)$' -and $p.Value -is [string]) { $pass = $p.Value }
}
if ($user -and $pass) { Try-User $user $pass }
foreach ($p in $obj.PSObject.Properties) {
if ($p.Value -is [psobject]) { Walk-Pairs $p.Value $user }
elseif ($p.Value -is [System.Collections.IEnumerable] -and -not ($p.Value -is [string])) {
foreach ($item in $p.Value) { if ($item -is [psobject]) { Walk-Pairs $item $user } }
}
}
}
Walk-Pairs $j $null
# Also try inquiry_robot from secrets against local and remote
$irUser = [string]$sec.mysql_user
$irPass = [string]$sec.mysql_password
Write-Output "=== inquiry_robot against local/remote ==="
Try-User $irUser $irPass
$outR = & $mysql -h10.206.0.14 "-u$irUser" "-p$irPass" -e "SELECT 'remote_ok' AS r" 2>&1 | Out-String
if ($outR -match "remote_ok") { Write-Output "REMOTE_IR_OK" } else {
Write-Output ("REMOTE_IR_FAIL " + (($outR -split "`r?`n" | Where-Object { $_ -match "ERROR" } | Select-Object -First 1)))
}
# Also try ytd_jeecg from credentials against local for CREATE DATABASE
Write-Output "=== login-path / defaults ==="
$mylogin = Join-Path $env:APPDATA "MySQL\.mylogin.cnf"
Write-Output ("mylogin_exists=" + (Test-Path $mylogin))
Get-ChildItem "C:\yutongda\secure" -Filter "*mysql*" -ErrorAction SilentlyContinue | ForEach-Object { $_.Name }
Get-ChildItem "C:\yutongda\secure" -Filter "*root*" -ErrorAction SilentlyContinue | ForEach-Object { $_.Name }
Write-Output "=== phase-c script mentions ==="
Select-String -Path "C:\yutongda\ops\phase-c-secure-app-db-credentials.ps1" -Pattern "root|CREATE DATABASE|inquiry|password" |
Select-Object -First 20 |
ForEach-Object { $_.Line.Trim().Substring(0, [Math]::Min(120, $_.Line.Trim().Length)) }
# Who listens 3306
Write-Output "=== 3306 owner ==="
Get-NetTCPConnection -LocalPort 3306 -State Listen -ErrorAction SilentlyContinue |
Select-Object -First 3 LocalAddress,OwningProcess |
ForEach-Object {
$p = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
"{0} pid={1} path={2}" -f $_.LocalAddress, $_.OwningProcess, $p.Path
}