Загрузка данных


# ============================================================
# Browser Saved Password Audit
# PowerShell 2.0 compatible
#
# Проверяет наличие реально сохраненных паролей:
#   - Google Chrome
#   - Microsoft Edge
#   - Yandex Browser
#
# Требуется sqlite3.exe рядом со скриптом.
#
# Результаты:
#   COMPUTERNAME_PASS.txt
#   COMPUTERNAME_FAIL.txt
#   COMPUTERNAME_PASS_WITH_WARNINGS.txt
#   COMPUTERNAME.marker
#   summary.txt
# ============================================================


# ------------------------------------------------------------
# НАСТРОЙКИ
# ------------------------------------------------------------

$ResultDirectory = "\\10.68.16.120\report\browser_password_check"
$SummaryFile = Join-Path $ResultDirectory "summary.txt"

$ComputerName = $env:COMPUTERNAME

# sqlite3.exe должен лежать рядом со скриптом
$ScriptDirectory = Split-Path -Parent $MyInvocation.MyCommand.Definition
$SqliteExe = Join-Path $ScriptDirectory "sqlite3.exe"

# Только сетевой marker
$MarkerFile = Join-Path $ResultDirectory ($ComputerName + ".marker")

# Временная директория
$TempDirectory = Join-Path $env:TEMP "BrowserPasswordAudit"

# ------------------------------------------------------------
# MARKER CHECK
# ------------------------------------------------------------

Write-Host "==============================================="
Write-Host " Browser Saved Password Audit"
Write-Host " Computer: $ComputerName"
Write-Host "==============================================="

Write-Host ""
Write-Host "Checking network marker..."

if (Test-Path $MarkerFile)
{
    Write-Host "Marker already exists:"
    Write-Host $MarkerFile
    Write-Host ""
    Write-Host "Audit was already completed. Exiting."
    exit 0
}

Write-Host "Marker not found. Starting audit..."
Write-Host ""


# ------------------------------------------------------------
# ПРОВЕРКА SQLITE
# ------------------------------------------------------------

if (-not (Test-Path $SqliteExe))
{
    Write-Host "ERROR: sqlite3.exe was not found:"
    Write-Host $SqliteExe
    exit 1
}


# ------------------------------------------------------------
# ПОДГОТОВКА TEMP
# ------------------------------------------------------------

if (Test-Path $TempDirectory)
{
    Remove-Item $TempDirectory -Recurse -Force -ErrorAction SilentlyContinue
}

New-Item -Path $TempDirectory -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null


# ------------------------------------------------------------
# ОБЩИЕ ПЕРЕМЕННЫЕ
# ------------------------------------------------------------

$TotalSavedPasswords = 0
$TotalWarnings = 0
$TotalFailures = 0

$Report = @()

$Report += "============================================================"
$Report += " Browser Saved Password Audit"
$Report += " Computer: $ComputerName"
$Report += " Date: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
$Report += "============================================================"
$Report += ""

$Report += "The audit checks actual saved password records in Chromium"
$Report += "Login Data SQLite databases."
$Report += "Password values are NOT extracted or written to the report."
$Report += ""


# ------------------------------------------------------------
# ПОЛУЧЕНИЕ ПРОФИЛЕЙ WINDOWS
# ------------------------------------------------------------

$UserDirectories = Get-ChildItem "C:\Users" -Directory -ErrorAction SilentlyContinue

if ($null -eq $UserDirectories)
{
    $Report += "WARNING: Could not enumerate C:\Users"
    $TotalWarnings++
}


# ------------------------------------------------------------
# ФУНКЦИЯ ПРОВЕРКИ Login Data
# ------------------------------------------------------------

function Test-LoginDatabase
{
    param(
        [string]$BrowserName,
        [string]$DatabasePath,
        [string]$UserName,
        [string]$ProfileName
    )

    $Result = New-Object PSObject
    $Result | Add-Member NoteProperty Browser $BrowserName
    $Result | Add-Member NoteProperty User $UserName
    $Result | Add-Member NoteProperty Profile $ProfileName
    $Result | Add-Member NoteProperty Database $DatabasePath
    $Result | Add-Member NoteProperty PasswordCount 0
    $Result | Add-Member NoteProperty Status "PASS"
    $Result | Add-Member NoteProperty Message ""

    if (-not (Test-Path $DatabasePath))
    {
        $Result.Message = "Login Data not found"
        return $Result
    }

    # --------------------------------------------------------
    # Копируем БД во временный файл.
    # Браузер может держать оригинал открытым.
    # --------------------------------------------------------

    $SafeUser = $UserName -replace '[^a-zA-Z0-9_-]', '_'
    $SafeBrowser = $BrowserName -replace '[^a-zA-Z0-9_-]', '_'
    $SafeProfile = $ProfileName -replace '[^a-zA-Z0-9_-]', '_'

    $RandomPart = [Guid]::NewGuid().ToString("N")

    $TempDatabase = Join-Path $TempDirectory (
        $SafeBrowser + "_" +
        $SafeUser + "_" +
        $SafeProfile + "_" +
        $RandomPart + ".db"
    )

    try
    {
        Copy-Item $DatabasePath $TempDatabase -Force -ErrorAction Stop
    }
    catch
    {
        $Result.Status = "WARNING"
        $Result.Message = "Could not copy Login Data: " + $_.Exception.Message
        return $Result
    }

    # --------------------------------------------------------
    # SQL:
    #
    # Считаем только реальные записи с непустым password_value.
    #
    # blacklisted_by_user = 0
    # исключает сайты, для которых сохранение пароля
    # было запрещено пользователем.
    # --------------------------------------------------------

    $SqlQuery = "SELECT COUNT(*) FROM logins WHERE blacklisted_by_user = 0 AND password_value IS NOT NULL AND length(password_value) > 0;"

    try
    {
        $Output = & $SqliteExe -readonly $TempDatabase $SqlQuery 2>&1

        $ExitCode = $LASTEXITCODE

        if ($ExitCode -ne 0)
        {
            $Result.Status = "WARNING"
            $Result.Message = "SQLite query failed: " + (($Output | Out-String).Trim())
            return $Result
        }

        $CountText = (($Output | Out-String).Trim())

        $Count = 0

        if (-not [int]::TryParse($CountText, [ref]$Count))
        {
            $Result.Status = "WARNING"
            $Result.Message = "SQLite returned an unexpected value: " + $CountText
            return $Result
        }

        $Result.PasswordCount = $Count

        if ($Count -gt 0)
        {
            $Result.Status = "FAIL"
            $Result.Message = "Saved passwords found: " + $Count
        }
        else
        {
            $Result.Status = "PASS"
            $Result.Message = "No saved passwords found"
        }
    }
    catch
    {
        $Result.Status = "WARNING"
        $Result.Message = "Exception while checking SQLite database: " + $_.Exception.Message
    }
    finally
    {
        if (Test-Path $TempDatabase)
        {
            Remove-Item $TempDatabase -Force -ErrorAction SilentlyContinue
        }
    }

    return $Result
}


# ------------------------------------------------------------
# ПРОВЕРКА БРАУЗЕРОВ
# ------------------------------------------------------------

$Browsers = @()


# Google Chrome
$Browsers += New-Object PSObject -Property @{
    Name = "Google Chrome"
    RelativePath = "AppData\Local\Google\Chrome\User Data"
}


# Microsoft Edge
$Browsers += New-Object PSObject -Property @{
    Name = "Microsoft Edge"
    RelativePath = "AppData\Local\Microsoft\Edge\User Data"
}


# Yandex Browser
$Browsers += New-Object PSObject -Property @{
    Name = "Yandex Browser"
    RelativePath = "AppData\Local\Yandex\YandexBrowser\User Data"
}


foreach ($UserDirectory in $UserDirectories)
{
    $UserName = $UserDirectory.Name

    # --------------------------------------------------------
    # Служебные профили Windows пропускаем
    # --------------------------------------------------------

    if ($UserName -eq "All Users")
    {
        continue
    }

    if ($UserName -eq "Default")
    {
        continue
    }

    if ($UserName -eq "Default User")
    {
        continue
    }

    if ($UserName -eq "Public")
    {
        continue
    }


    $Report += "------------------------------------------------------------"
    $Report += "Windows user: $UserName"
    $Report += "------------------------------------------------------------"


    foreach ($Browser in $Browsers)
    {
        $BrowserUserData = Join-Path $UserDirectory.FullName $Browser.RelativePath

        if (-not (Test-Path $BrowserUserData))
        {
            $Report += ""
            $Report += $Browser.Name + ": not installed / profile not found"
            continue
        }


        # ----------------------------------------------------
        # Получаем профили браузера:
        #
        # Default
        # Profile 1
        # Profile 2
        # etc.
        # ----------------------------------------------------

        $BrowserProfiles = Get-ChildItem $BrowserUserData -Directory -ErrorAction SilentlyContinue

        if ($null -eq $BrowserProfiles)
        {
            $Report += ""
            $Report += $Browser.Name + ": no browser profiles found"
            continue
        }


        foreach ($Profile in $BrowserProfiles)
        {
            $LoginData = Join-Path $Profile.FullName "Login Data"

            # Проверяем только если Login Data существует
            if (-not (Test-Path $LoginData))
            {
                continue
            }


            $Result = Test-LoginDatabase `
                -BrowserName $Browser.Name `
                -DatabasePath $LoginData `
                -UserName $UserName `
                -ProfileName $Profile.Name


            $Report += ""

            $Report += "Browser: $($Result.Browser)"
            $Report += "User: $($Result.User)"
            $Report += "Profile: $($Result.Profile)"
            $Report += "Database: $($Result.Database)"
            $Report += "Saved passwords: $($Result.PasswordCount)"
            $Report += "Status: $($Result.Status)"
            $Report += "Message: $($Result.Message)"


            # ------------------------------------------------
            # Общая статистика
            # ------------------------------------------------

            if ($Result.Status -eq "FAIL")
            {
                $TotalFailures++
                $TotalSavedPasswords += $Result.PasswordCount
            }
            elseif ($Result.Status -eq "WARNING")
            {
                $TotalWarnings++
            }
        }
    }

    $Report += ""
}


# ------------------------------------------------------------
# ИТОГОВЫЙ СТАТУС
# ------------------------------------------------------------

$Report += ""
$Report += "============================================================"
$Report += " SUMMARY"
$Report += "============================================================"

$Report += "Total saved password records found: $TotalSavedPasswords"
$Report += "Failed checks: $TotalFailures"
$Report += "Warnings: $TotalWarnings"
$Report += ""


if ($TotalFailures -gt 0)
{
    $OverallStatus = "FAIL"
}
elseif ($TotalWarnings -gt 0)
{
    $OverallStatus = "PASS_WITH_WARNINGS"
}
else
{
    $OverallStatus = "PASS"
}


$Report += "OVERALL STATUS: $OverallStatus"
$Report += "============================================================"


# ------------------------------------------------------------
# УДАЛЯЕМ TEMP
# ------------------------------------------------------------

if (Test-Path $TempDirectory)
{
    Remove-Item $TempDirectory -Recurse -Force -ErrorAction SilentlyContinue
}


# ------------------------------------------------------------
# СОЗДАНИЕ ВРЕМЕННОГО ОТЧЕТА НА СЕТИ
# ------------------------------------------------------------

$TemporaryReport = Join-Path $ResultDirectory ($ComputerName + ".txt")

$FinalReport = Join-Path $ResultDirectory (
    $ComputerName + "_" + $OverallStatus + ".txt"
)


$NetworkReportSaved = $false


try
{
    $ReportDirectoryExists = Test-Path $ResultDirectory

    if (-not $ReportDirectoryExists)
    {
        throw "Report directory is not accessible."
    }


    # Если старый временный файл остался
    if (Test-Path $TemporaryReport)
    {
        Remove-Item $TemporaryReport -Force -ErrorAction Stop
    }


    $Report | Out-File `
        -FilePath $TemporaryReport `
        -Encoding UTF8 `
        -Force `
        -ErrorAction Stop


    if (-not (Test-Path $TemporaryReport))
    {
        throw "Report file was not created."
    }


    # --------------------------------------------------------
    # Переименование с добавлением статуса
    # --------------------------------------------------------

    if (Test-Path $FinalReport)
    {
        Remove-Item $FinalReport -Force -ErrorAction Stop
    }


    Rename-Item `
        -Path $TemporaryReport `
        -NewName ($ComputerName + "_" + $OverallStatus + ".txt") `
        -ErrorAction Stop


    if (-not (Test-Path $FinalReport))
    {
        throw "Final report was not found after rename."
    }


    $NetworkReportSaved = $true
}
catch
{
    Write-Host ""
    Write-Host "ERROR: Could not save report to network."
    Write-Host $_.Exception.Message
}


# ------------------------------------------------------------
# ЕСЛИ ОТЧЕТ СОХРАНЕН НА СЕТИ —
# СОЗДАЕМ MARKER
# ------------------------------------------------------------

$MarkerCreated = $false


if ($NetworkReportSaved)
{
    try
    {
        if (Test-Path $MarkerFile)
        {
            Remove-Item $MarkerFile -Force -ErrorAction SilentlyContinue
        }


        New-Item `
            -Path $MarkerFile `
            -ItemType File `
            -Force `
            -ErrorAction Stop | Out-Null


        if (Test-Path $MarkerFile)
        {
            $MarkerCreated = $true
        }
    }
    catch
    {
        Write-Host ""
        Write-Host "WARNING: Report saved, but marker could not be created."
        Write-Host $_.Exception.Message
    }
}


# ------------------------------------------------------------
# SUMMARY.TXT
# ------------------------------------------------------------

if ($NetworkReportSaved)
{
    try
    {
        Add-Content `
            -Path $SummaryFile `
            -Value ($ComputerName + " - " + $OverallStatus) `
            -ErrorAction Stop
    }
    catch
    {
        Write-Host ""
        Write-Host "WARNING: Could not update summary.txt"
        Write-Host $_.Exception.Message
    }
}


# ------------------------------------------------------------
# ФИНАЛЬНЫЙ ВЫВОД
# ------------------------------------------------------------

Write-Host ""
Write-Host "==============================================="
Write-Host " AUDIT COMPLETE"
Write-Host "==============================================="

Write-Host "Computer: $ComputerName"
Write-Host "Saved passwords found: $TotalSavedPasswords"
Write-Host "Warnings: $TotalWarnings"
Write-Host "Overall status: $OverallStatus"

if ($NetworkReportSaved)
{
    Write-Host "Report: $FinalReport"
}
else
{
    Write-Host "Report was NOT saved to network."
}

if ($MarkerCreated)
{
    Write-Host "Marker: CREATED"
}
else
{
    Write-Host "Marker: NOT CREATED"
}

Write-Host "==============================================="