146 lines
6.8 KiB
PowerShell
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
|
|
}
|