Update 04.09.2009: относительно пункта 3 данного поста обязательно прочтите обновление информации по нему: Секреты Software Restriction Policies (часть 3) (так же прочитать пункт 3). Т.е. вы должны исключить LNK файлы из проверки политикой и, следовательно, исключения для них создавать не надо.

Так же прочтите обновление по этой же ссылки относительно использования переменных окружения в правилах SRP.


Наблюдая за сообщениями на форумах (а так же по комментариям в моём блоге) пришёл к выводу, что очень немногие понимают всю значимость Software Restriction Policies и далеко не каждый обладает навыками эффективного управления данной политикой. Я уже освещал некоторые вопросы по данной теме, но тем не менее пробелы до сих пор есть, а так же есть новые трюки для обхода политики.

Итак, для тех, кто ещё не в курсе, о чём идёт речь предлагается прочитать:

В данном посте я планирую раскрыть секреты расширения SCF, трюки с hh.exe, рассмотреть вопросы унификации правил и два важных момента в управлении политикой.

1) Расширение SCF – это командный файл, который обрабатывается стандартным шеллом (он же explorer.exe). Данные файлы достаточно хитры, поскольку проводник (explorer) даже в режиме показа известных расширений никогда не показывает его. Это ещё пол беды. Основная угроза, которая может от них исходить – встраиваемость в файлы. В двух словах это выглядит как прописывание своего кода в любой файл (будь то текстовый, графический, медиафайл и т.д.), подмена расширения и иконки. Синтаксис SCF файлов очень похож на синтаксис INI и INF файлов. Т.е. он состоит из секций, заключённых в квадратные скобки и элементы секций:

[section]
parameter=value
[section2]
parameter2=value2
parameter3=value3

Самый знакомый нам файл такого типа – Show Desktop в панели quick launch. Это самый настоящий SCF файл, который имеет следующее содержание:

[shell]
Command=2
IconFile=explorer.exe,3
[TaskBar]
Command=ToggleDesktop

как видно отсюда, мы можем изменять иконку файла на любую. Очень полезно для маскировки файла под другой тип файла. И в секции TaskBar мы видим вызов внутренней команды explorer.exe – ToggleDesktop. Напомню, что эти файлы обрабатываются только оболочкой explorer.exe. Если сохранить эту часть в текстовом файле и указать расширение SCF, то вы получите сворачивалку окон. Если, к примеру, в секции Command указать другую команду, например, Explorer, то при двойном клике на него запустится сам explorer.exe:

[shell]
Command=2
IconFile=explorer.exe,3
[TaskBar]
Command=Explorer

А теперь как встраивать код в другие файлы. Берём любой произвольный файл, например картинку в формате JPEG, открываем его в текстовом редакторе, но лушче будет в FAR и в самое начало или самый конец файла пишем:

[shell]
Command=2
IconFile=imageres.dll,66
[TaskBar]
Command=Explorer

сохраняем изменения и переименовываем файл в image.jpeg.scf.В Windows Vista/Windows Server 2008 мы получим файл размером с картинку, подходящее расширение и значок JPEG файла (хвост .scf будет скрыт от нас в любом случае). Если по нему нажать 2 раза, то запустится explorer. Причём никаких ошибок вы не получите, потому что остальное содержимое файла будет проигнорировано и будет выполнена только та часть кода, которую мы добавили. Искать иконки можно различными редакторами ресурсов, как Restorator или ResHacker. Просто в категории Icon или IconGroup выбираете нужный номер иконки и вставляете этот номер в параметр значка. При этом скорее всего потребуется уменьшить это число на единицу, поскольку Explorer будет считать значки с нуля, в то время как редакторы ресурсов считают с единицы. Но это уже детали.

К сожалению я в данный момент не обладаю сведениями о полном функционале файлов SCF, но доподлинно известно, что ими можно манипулировать как оболочкой explorer.exe, так и браузером Internet Explorer. Поэтому я пока не могу сказать на сколько это может быть опасно. Но неадекватное поведение файла при его запуске может доставить массу хлопот как самим пользователям, так и администраторам.

Следовательно имеет смысл при развёртывании политики SRP включить данное расширение в список блокируемых, а значок Show Desktop.scf разрешить по хэшу! Это важно, поскольку разрешение пути позволит пользователю модифицировать файл и исполнить его.

Примечание: в новых версиях Windows начиная от Windows Vista значок ShowDesktop.scf был сменён с SCF файла на Show Desktop.lnk, следовательно для этих версий ОС достаточно будет просто включить данное расширение в список и всё. Хотя SCF файл в Windows Vista будет работать :)

2)hh.exe – исполняемый файл, который обрабатывает фалы справки CHM. CHM – это скомпилированный HTML файл, следовательно может содержать потенциально уязвимый код. Но тут, казалось бы, бояться нечего, поскольку расширение CHM по умолчанию мониторится политикой SRP и его запуск из пользовательского окружения недоступен. Но это не совсем так. В действительности пользователь может обойти ограничение SRP и спокойно запускать любые CHM файлы. Обходится данное ограничение очень просто:

Start –> Run… –> hh.exe path\helpfile.chm

и всё. К сожалению, я не нашёл метода, как гибко бороться с этим, только явно запрещать политикой SRP запуск программы hh.exe. Ещё к бОльшему сожалению блокировать CHM файлы в системах до Windows Vista тяжело, поскольку почти вся справка Windows там написана в CHM. Однако, в Windows Vista и выше встроенная справка уже не базируется на CHM файлах, поэтому в них мы можем со значительно меньшими последствиями просто блокировать hh.exe. Но тут можно упереться в вопрос совместимости сторонних приложений, которые зачастую содержат справку в CHM файлах. Поэтому, я не призываю блокировать hh.exe, но просто исследовать данный вопрос в каждом индивидуальном случае отдельно.

Примечание: на данный момент мне неизвестно о других расширениях, которые подвержены данной проблемы. Я тестировал набор расширений, но добиться подобного результата не удалось. Хочется надеяться, что это единственное такое хитрое расширение :)

3) унификация и стандартизация создания исключений в политику SRP. Прочитав топик на форуме TechNet – сслыка, я в очередной раз пришёл к мнению, что человек первый раз столкнувшись с SRP не обладает навыками эффективного создания исключений. Это и не удивительно вобщем, поэтому я в темах про SRP ставлю перед собой задачу – рассказать про Best Practices для эффективной реализации этой политики.

За время практики я (и не только) смог выработать для себя общий концепт создания исключений, который звучит примерно так: следует предельно максимально использовать переменные окружения и ключи реестра вместо абсолютных путей.

Политика SRP даёт нам широкие возможности для этого, это и использование системных переменных окружений %variable%, но и чтение этих переменных из реестра. Повторюсь, что данная концепция решает несколько задач, как унификация правил для различных платформ и обход локализованных барьеров. В качестве примеров можно привести различия некоторых стандартных путей в ОС Windows XP и Windows Vista.

  • Например, профили пользователей раньше хранились в C:\Documents and Settings, а в Vista – C:\Users. Уже здесь мы видим эффект от использования системных переменных окружения как %userprofile%.
  • Но если в сети используются и локализованные системы (смешанные), то системные переменные доставят нам хлопот, поскольку придётся часто дублировать исключения пути: %userprofile%\Desktop\*.lnk и %userprofile\Рабочий стол\*.lnk. Это так же существенный барьер, который обойти стандартными переменными окружения практически нерентабельным. И вот здесь мы видим эффект от чтения нужных путей из реестра.

Чтобы администраторам дать хорошую отправную точку, я решил поделиться стандартным набором разрешений, который будет отвечать следующим требованиям:

  • пользователи могут запускать исполняемые файлы из системной папки Windows и Program Files
  • пользователи могут запускать ярлыки из своего пользовательского Start Menu
  • пользователи могут запускать ярлыки из Start Menu общего профиля, который известен как AllUsersProfile
  • пользователи могут запускать ярлыки со своего и AllUsers рабочего стола
  • пользователи могут запускать ярлыки из своего меню быстрого запуска (Quick Launch)
  • пользователи не могут запускать исполняемые файлы из папки спулера и системного Temp.

И вот такой список исключений нам потребуется:

Unrestricted - %systemroot%
Unrestricted - %programfiles%
Unrestricted - %userprofile%\Application Data\Microsoft\Internet Explorer\Quick Launch\*.lnk – (Windows XP/2003 only)
Unrestricted - %HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\AppData%/Microsoft/Internet Explorer/Quick Launch/*.lnk – (Windows Vista/2008 или выше)
Unrestricted - %HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Desktop%*.lnk
Unrestricted - %HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Programs%*.lnk
Unrestricted - %HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Programs%*/*.lnk
Unrestricted - %HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Programs%*/*/*.lnk
Unrestricted - %HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Common Administrative Tools%*.lnk
Unrestricted - %HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Common Desktop%*.lnk
Unrestricted - %HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Common Programs%*.lnk
Unrestricted - %HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Common Programs%*/*.lnk
Unrestricted - %HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Common Programs%*/*/*.lnk
Dissalowed - %HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers%
Dissalowed - %systemroot%\Temp

Здесь хочу сделать несколько комментариев:

  • Мы явно запрещаем запуск недозволенных приложений из системных папок, куда пользователи по умолчанию имеют право записи и исполнения. Это C:\Windows\Temp и папка спулера, поскольку они явно разрешаются первым правилом. Следовательно, чтобы предотвратить запуск файлов из этих папок мы явно их запрещаем отдельными правилами.
  • я указал 2 пути для QuickLaunch для систем Windows XP и Windows Vista. Это обусловленно тем, что в Windows XP/Windows Server 2003 максимальная длина исключения для пути ограничена 133 знаками и вот такой “финт ушами”, который я привёл для Windows Vista, не проходит. В новых ОС этот недостаток устранён.
  • здесь я не привёл явного запрещения для hh.exe, т.к. это стартовый набор правил, которое обеспечивает достаточную совместимость с другими приложениями.
  • Мы разрешаем несколько уровней вложения для Start Menu, поскольку всё это меню как правило состоит из 3-х уровней вложения.

Примечание: для бизнес-приложений, которые как правило инсталлируются на отличный от системного том (обычно D:\) и для них рекомендуется делать исключения по хешу, нежели по пути. Это полезно по двум причинам:

  • зачастую бизнес-приложения пишутся “быдлокодерами” (простите уж за такую лексику, но это суровая правда), которые требуют разрешения записи пользователям в папку с исполняемым модулем. Спастись от заражения этого модуля пользователями можно только хешем. Это, к сожалению, актуальная проблема, которую нужно решать примерно так: “разработчиков софта, требующего админских привилегий, нужно силой пересаживать на OpenBSD, и не кормить дошираком до тех пор, пока их г***ософт не начнет там работать в ANSi-режиме на библиотеке ncurses” © Amin
  • в случае, если случится факт заражения (в данном случае только от лица неосторожного администратора), то высока вероятность, что вирус постарается инфицировать все .exe файлы в системе и по изменившемуся хешу это можно быстро вычислить (приложение больше не запустится) и в кратчайшие сроки вывести поражённую систему из сети для дальнейшего расследования инцидента.

4) обновление ярлыков временного отключения и включения SRP. Я считаю удобным держать на рабочем столе администратора ярлыки, которые запускают REG файлы, которые в свою очередь временно отключают и возвращают политику в исходную позицию. Если для одной рабочей станции – это менее критично, то в условиях домена отключать политику для установки/обновления ПО на уровне GPO – крайне нецелесообразно. Я уже публиковал в своём предыдущем блоге тексты REG файлов, но без учёта папки Temp.

Т.к. мы явно запретили запуск недозволенных расширений из папки C:\Windows\Temp, то даже временная деактивация политики ярлыками не позволит нам сделать некоторые действия, например, установка драйвера или приложения, если установщик сперва распаковывает INF файлы в эту папку, поскольку перевод глобального режима политики в Unrestricted не отменит этот явный запрет. Следовательно мы помимо перевода политики в Unrestricted должны вывести администраторов из под действия политики. Вот REG файлы для временного отключения и включения политики:

SRP_Disable.reg

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Safer\CodeIdentifiers]
"DefaultLevel"=dword:00040000
"PolicyScope"=dword:00000001

SRP_Enable.reg

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Safer\CodeIdentifiers]
"DefaultLevel"=dword:00000000
"PolicyScope"=dword:00000000

5) обновление политики. В процессе работы с ярлыками обнаружился один неприятный факт: если отключить политику реестром и не включить обратно, то после перезагрузки машины кроме включения политики реестром обратно потребуется выполнить ещё 2 перезагрузки. Как вы, наверное, поняли, эти ключи реестра не изменяют саму политику, а только её поведение. Следовательно пришлось решать вопрос возвращения политики в исходное состояние при перезагрузках при помощи групповых политик. По умолчанию, политики перезаписывают соответствующие ключи реестра только при изменение состояния политики (чего мы ярылками не делаем). Т.к. SRP у нас основывается на реестре (все её правила и настройки хранятся именно в реестре), то нужно задать принудительное переприменение политики при перезагрузках (или периодических обновлений политики в домене). Делается это в Group Policy:

Computer Configuration\Administrative Templates\System\Group Policy\Registry policy processing

и этот элемент необходимо выставить в Enabled внутри поставить галочку в Process even if the Group Policy objects have not changed. В таком случае даже если администратор забудет реестром обратно включить политику SRP, то политика процессинга сама вернёт её в исходное состояние – Disallowed.

-------------------

Сегодня мы рассмотрели очередную партию насущных вопросов по настройке и управлению политикой Software Restriction Policies. Я недеюсь, что этот материал будет полезен как начинающим (тех, кто только начинает работать с политикой SRP) и опытным администраторам, которые уже используют политики SRP в своих сетях.

Tuesday, February 24, 2009 4:09:51 PM (FLE Standard Time, UTC+02:00)   Comments [8]    

 

В предыдущем посте я показал, как можно легко и удобно подписывать скрипты и как усилить безопасность их исполнения, а так же предупредить несанкционированное изменение кода. Но, как я уже отметил, подписывать каждый раз скрипт из консоли не особо удобно. Например, если вы занимаетесь финальной отладкой скрипта в редакторе, как PowerGUI или PowerShell ISE, то в конце отладки вы просто сохраняете скрипт и закрываете редактор. Понятно, что ещё отдельно запускать консоль PowerShell будет неудобно. Поэтому я написал небольшой скрипт с установщиком, который позволит из проводника по правому нажатию на PS1 файл вы сможете подписывать скрипты.

Чтобы поместить свой элемент в контекстное меню при нажатии на PS1 файлы (на других типах файлов его не будет) нам нужно добавить некоторые значения в реестр по пути:

HKLM\Software\Classes\Microsoft.PowerShellScript.1\Shell

подключ с названием нашего элемента – Sign It и в нём уже команду, которая будет отрабатываться при нажатии на него. Я решил не изобретать велосипед и воспользовался приёмами, которые использовал ещё в старом блоге: Полезная безделушка Hash SHA1 на PowerShell. Шапка по сути осталась та же, только тело (исполняемый модуль) изменился. И вот, что получилось для PowerShell 1.0 (для V2 опубликую ниже):

#####################################################################
# Sign PS1 Script.ps1
# Version 1.5
#
# Automatically sign PowerShell script from .PS1 file context menu
# Fully compatible with PowerShell 1.0
#
# Vadims Podans (c) 2009
# http://www.sysadmins.lv/
#####################################################################

$FilePath = Read-Host "Specify path to hold SignIt.PS1"
if (Test-Path $FilePath) {
    $FilePath = $FilePath + "\" + "SignIt.ps1"
    $RegPath = "Registry::HKLM\Software\Classes\Microsoft.PowerShellScript.1\Shell\Sign It\command"
    $RegValue = "C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe -noninteractive -noprofile -command $FilePath '%1'"
    New-Item -Path $RegPath -Force -ErrorAction SilentlyContinue
    if (Test-Path $RegPath) {
        New-ItemProperty -Path $RegPath -Name "(Default)" -Value $RegValue
        New-Item -ItemType file -Path $FilePath -Force
        if (Test-Path $FilePath) {
            $exefile = 'param ($file)
$cp = new-object Microsoft.CSharp.CSharpCodeProvider
$cpar = New-Object System.CodeDom.Compiler.CompilerParameters
$HideWindow = 0x0080
$ShowWindow = 0x0040
$Code = @"
using System;
using System.Runtime.InteropServices;
namespace Win32API
{
    public class Window
    {
        [DllImport("user32.dll")]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
    }
}
"@
$cp.CompileAssemblyFromSource($cpar, $code)
$PSHandle = (Get-Process –id $pid).MainWindowHandle
[Win32API.Window]::SetWindowPos($PSHandle, 0, 0, 0, 0, 0, $HideWindow)
function _msgbox_ ($title, $text, $type = "None") {
    [void][reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
    $msg = [Windows.Forms.MessageBox]::Show($text, $title, [Windows.Forms.MessageBoxButtons]::ok, 
    [Windows.Forms.MessageBoxIcon]::$type)
}
$cert = @(dir cert:\currentuser\my -codesigning)[0]
if (!$cert) {
    _msgbox_ -title "Error" -text "Here is no valid signing certificate!" -type "Error"
    exit
}
$status = Set-AuthenticodeSignature "$file" $cert -TimestampServer "http://timestamp.verisign.com/scripts/timstamp.dll"
if ($status.status -eq "Valid") {
    _msgbox_ -title "Success" -text "Script is now signed! Enjoy!"
} else {
    _msgbox_ -title "Error" -text $status.StatusMessage -type "Error"
}
exit'
            Set-Content -Path $FilePath -Value $exefile
            &$filepath $filepath
        }
        else {
            Write-Warning "Unable to create file in: $FilePath. Path is incorrect or you haven't sufficient permissions"
        }
    }
    else {
        Write-Warning "Unable to create registry key. May be you haven't sufficient permissions to HKLM hive"
    }
}

Данный скрипт является установщиком. Для реализации всех функций достаточно запустить этот скрипт и по требованию указать путь, где будет находиться рабочий скрипт (содержимое переменной $exefile).

Примечание: такой метод установки справедлив только для V2. При использовании PowerShell 1.0 установщик следует запускать непосредственно из консоли PowerShell.

Заметка: Здесь следует обратить внимание на блок кода, который идёт после PARAM в переменной $exefile. Данный блок кода скрывает саму консоль PowerShell и показывает только графическую часть, которая исполняется из скрипта. Функция скрытия консоли честно взята из блога Васи Гусева: Win32 API из PowerShell 1.0. Данный метод работает как в PowerShell V2 CTP3, так и в PowerShell 1.0.

И вариант скрипта для PowerShell V2:

#####################################################################
# Sign PS1 Script.ps1
# Version 1.5
#
# Automatically sign PowerShell script from .PS1 file context menu
#
# Require PowerShell V2, not compatible with PowerShell 1.0
#
# Vadims Podans (c) 2009
# http://www.sysadmins.lv/
#####################################################################
#requires -Version 2.0

$FilePath = Read-Host "Specify path to hold SignIt.PS1"
if (Test-Path $FilePath) {
    $FilePath = $FilePath + "\" + "SignIt.ps1"
    $RegPath = "Registry::HKLM\Software\Classes\Microsoft.PowerShellScript.1\Shell\Sign It\command"
    $RegValue = "C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -noprofile -command $FilePath '%1'"
    New-Item -Path $RegPath -Force -ErrorAction SilentlyContinue
    if (Test-Path $RegPath) {
        New-ItemProperty -Path $RegPath -Name "(Default)" -Value $RegValue
        New-Item -ItemType file -Path $FilePath -Force
        if (Test-Path $FilePath) {
            $exefile = 'param ($file)
function _msgbox_ ($title, $text, $type = "None") {
    [void][reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
    $msg = [Windows.Forms.MessageBox]::Show($text, $title, [Windows.Forms.MessageBoxButtons]::ok,
    [Windows.Forms.MessageBoxIcon]::$type)
}
$cert = @(dir cert:\currentuser\my -codesigning)[0]
if (!$cert) {
    _msgbox_ -title "Error" -text "Here is no valid signing certificate!" -type "Error"
    exit
}
$status = Set-AuthenticodeSignature "$file" $cert -TimestampServer "http://timestamp.verisign.com/scripts/timstamp.dll"
if ($status.status -eq "Valid") {
    _msgbox_ -title "Success" -text "Script is now signed! Enjoy!"
} else {
    _msgbox_ -title "Error" -text $status.StatusMessage -type "Error"
}
exit'
            Set-Content -Path $FilePath -Value $exefile
            &$filepath $filepath
        }
        else {
            Write-Warning "Unable to create file in: $FilePath. Path is incorrect or you haven't sufficient permissions"
        }
    }
    else {
        Write-Warning "Unable to create registry key. May be you haven't sufficient permissions to HKLM hive"
    }
}

Так же, после копирования файла установщик попытается сразу подписать рабочий скрипт, чтобы этого не пришлось делать потом вручную. После этого в контекстном меню PS1 файлов будет элемент Sign It:

Context menu

И если у пользователя есть сертификат для подписи скриптов, то при нажатии на этот элемент получите сообщение:

Success signing message

В противном случае получите ошибку:

Failed signing message

Вот так можно значимо упростить процесс подписывания скриптов не открывая консоль PowerShell. Но если файлов будет много, то ничего не мешает основную часть кода засунуть в цикл Foreach. Например:

$cert = @(dir cert:\currentuser\my -codesigning)[0]
dir -Include *.ps1 -Recurse | %{Set-AuthenticodeSignature "$_" $cert}

На этом пока о подписывании скриптов всё, что я хотел рассказать.

Update 30.08.2009: немного переделал скрипты с учётом следующих поправок:

  • Добавлена возможность подписывания скриптов с использованием сервера времени VeriSign, который в подписи ставит подписанную VeriSign' ом цифровую метку времени.
  • Исправлены ошибки с выводом сообщений о результате операции. Например, если пользователь отменял операцию подписи, либо происходила другая ошибка в процессе подписывания, то появлялось сообщение о том, что файл подписан (хотя это было не так). Сейчас сделана проверка поля Status.
  • в связи с этим изменил код в посте на обновлённый.

И, собственно, кнопки для скачивания скриптов:

  • для PowerShell 1.0
  • для PowerShell 2.0
Tuesday, February 17, 2009 8:36:22 AM (FLE Standard Time, UTC+02:00)   Comments [2]    

 

Как известно практически всем пользователям PowerShell, в целях безопасности была введена политика запуска скриптов. Которая имеет 4 режима:

  • Restricted – запрещено исполнять любые скрипты, возможна работа только в интерактивной консоли
  • AllSigned – все скрипты должны быть криптографически подписаны
  • RemoteSigned – только полученные из недоверенных источников (например, интернет) скрипты должны быть подписаны
  • Unrestricted – наименее безопасный уровень, допускается исполнение любых скриптов

В версии V2 (пока ещё CTP3) скрипты PowerShell приравняли к исполняемым файлам и эти файлы стали неотключаемо мониториться политикой Software Restriction Policies. Я об этом уже писал ранее: PowerShell V2 и Software Restriction Policies. Поначалу меня это сильно напрягало, но после пришёл к мнению, что это правильно. Неправильно только то, что мы этого не видим (ps1 расширение нигде не фигурирует). С этим бороться можно двумя методами – явно указывать пути, откуда разрешён запуск .ps1 файлов или подписать их все.

Если компьютеров в сети больше одного, то самое идеальное для решения задачи будет наличие домена Active Directory и, по возможности, Enterprise Certification Authity (CA). Наличие домена решит массу задач, как распространение политики SRP в пределах домена, распространение сертификата в пределах домена, контроль версии сертификата, которым подписаны скрипты.

Итак, для начала нам нужно получить сертификат, которым будут подписываться скрипты. В целях безопасности следует создать ограниченную учётную запись пользователя из под которой администратор (в большинстве случаев) будет подписывать скрипты. В книге PowerShell In Action для генерации сертификата предлагается использовать makecert.exe, который входит в состав Visual Studio SDK, но как мне кажется более правильным будет использование CA. В Windows Server CA для этих целей есть уже готовый шаблон, который называется Code Signing. Но принципиальной разницы нету, каким инструментом вы будете генерировать сертификат и я опишу процесс получения сертификата с использованием доменного CA.

Если CA у нас уже установлен, то открываем оснастку Certification Authority и переходим в раздел Certificate Templates. Нажимаем правой кнопкой и выбираем Manage. Откроется редактор шаблонов. Если у вас Enterprise или Datacenter редакции Windows Server, то вы можете создать свой настроенный шаблон. Но я не вижу в этом необходимости. На данном этапе нам необходимо разрешить ограниченному пользователю запрашивать сертификаты этого шаблона. Для этого в закладке Security шаблона Code Signing нужно разрешить чтение и запрос сертификата ограниченному пользователю. Когда эта процедура проделана, редактор шаблонов можно закрыть. После чего в оснастке Certification Authority снова нажать правой кнопкой на разделе Certificate Templates –> New –> Certificate Template to Issue и в списке выбрать шаблон Code Signing.

После чего нужно залогиниться этим пользователем, запустить оснастку Certificate Manager (Start –> Run… –> certmgr.msc) и выполнить запрос сертификата. В списке шаблонов должен быть и добавленный нами Code Signing. Когда сертификат будет запрошен не надо закрывать оснастку сертификатов. Далее нам потребуется экспортировать открытую часть сертификата в x509 файл (с расширением .cer). Экспорт открытой части нам потребуется для проверки подписи и организации доверия подписи в пределах домена. Экспортированный сертификат необходимо теперь доставить администратору(-ам), который отвечает за групповую политику.

В групповых политиках (чаще всего в доменной политике) необходимо создать новую политику Software Restriction Policies и в Additiona Rules добавить правило сертификата (Certificate Rule) и указать экспортированный сертификат. Это необходимо затем, что PowerShell для проверки доверия сертификата ищет его в контейнере Trusted Publishers и только Software Restriction Policies позволяет централизовано распространять сертификаты в этот контейнер.

Теперь можно приступать к подписыванию скриптов. Для этих целей используется командлет Set-AuthenticodeSignature и синтаксис его такой:

Set-AuthenticodeSignature $file $cert

Где $file – путь к скрипту и $cert – объект сертификата, который получается следующим образом:

$cert = @(dir cert:\CurrentUser\My -codesigning)[0]

здесь мы явно указываем, что нам нужен сертификат, у которого в EKU (Enchanced Key Usage) указан Code Sgining. В нашем случае он будет всего 1. Но если их окажется несколько, то мы выберем самый первый. Вот как это будет выглядеть на практике:

[Desktop] Set-ExecutionPolicy allsigned [Desktop] Get-ExecutionPolicy AllSigned [Desktop] .\uptime.ps1 File C:\Documents and Settings\Signer\Desktop\uptime.ps1 cannot be loaded. The file C:\Documents and Settings\Signer \Desktop\uptime.ps1 is not digitally signed. The script will not execute on the system. Please see "get-help about_signing" for more details.. At line:1 char:13 + .\uptime.ps1 <<<< + CategoryInfo : NotSpecified: (:) [], PSSecurityException + FullyQualifiedErrorId : RuntimeException [Desktop] $cert = @(dir cert:\currentuser\my -codesigning)[0] [Desktop] $cert Directory: Microsoft.PowerShell.Security\Certificate::currentuser\my Thumbprint Subject ---------- ------- 42E5B32A19885C6ADCF9683BDA1C871E0FE5E0DB CN=Signer, CN=Users, DC=contoso, DC=com [Desktop] Set-AuthenticodeSignature uptime.ps1 $cert Directory: C:\Documents and Settings\Signer\Desktop SignerCertificate Status Path ----------------- ------ ---- 42E5B32A19885C6ADCF9683BDA1C871E0FE5E0DB Valid uptime.ps1 [Desktop] .\uptime.ps1 System Uptime for DC1 is: 1 days 4 hours 19 minutes 20 seconds [Desktop]

Я наглядно показал, как это работает. Мы сначала перевели политику исполнения скриптов в AllSigned и убедились, что неподписанный скрипт не исполняется. После чего я подписал этот скрипт и попробовал снова. Как видите, скрипт теперь исполнился.

Если не будет выполнено условие распространения сертификата посредством политики SRP в контейнер Trusted Publishers, то вы получите вот такое сообщение:

[Desktop] .\uptime.ps1 Do you want to run software from this untrusted publisher? File C:\Documents and Settings\Signer\Desktop\uptime.ps1 is published by CN=Signer, CN=Users, DC=contoso, DC=com and is not trusted on your system. Only run scripts from trusted publishers. [V] Never run [D] Do not run [R] Run once [A] Always run [?] Help (default is "D"):d File C:\Documents and Settings\Signer\Desktop\uptime.ps1 cannot be loaded because you have elected to no t run this software now. At line:1 char:12 + .\uptime.ps1 <<<< [Desktop]

Вот таким образом мы решаем задачу исполнения только проверенного набора скриптов. В этом смысле PowerShell 1.0 менее безопасный и удобный, поскольку мы не можем политикой SRP блокировать исполнение PS1 файлов как класс и имеем только один выход – принудительное подписывание скриптов. В версии V2 политика исполнения скриптов удобно интегрируется с SRP. Удобство интегрирования в том, что SRP помимо распространения сертификата в пределах домена так же на основе этого правила разрешает исполнять эти скрипты в обход общего ограничения на PS1 файлы.

В следующем посте я расскажу, как можно упростить процесс подписывания скриптов. Так что не отключаемся :)

Monday, February 16, 2009 8:56:08 AM (FLE Standard Time, UTC+02:00)   Comments [0]    

 

Этот скрипт написал скорее для себя, но весьма полезный. В Windows XP/Windows Server 2003 аптайм можно было легко посмотреть в свойствах сетевого подключения, которое как правило работает постоянно. Но в Windows Vista/Windows Server 2008 до него добираться далеко. Смотреть в Task Manager не удобно, поскольку он показывает аптайм в часах, а в уме высчитывать дни как-то неудобно. Вот и набросал функцию, которую положил себе в профиль:

function Get-SystemUptime ($computer = "$env:computername") {
    $lastboot = [System.Management.ManagementDateTimeconverter]::ToDateTime(
    "$((gwmi  Win32_OperatingSystem -computername $computer).LastBootUpTime)")
    $uptime = (Get-Date) - $lastboot
    Write-Host "System Uptime for $computer is: " $uptime.days "days" $uptime.hours `
    "hours" $uptime.minutes "minutes" $uptime.seconds "seconds"
}

Концепция очень простая – берётся дата и время последней загрузки системы и вычитается из текущей даты и времени. Здесь важно отметить, что WMI возвращает нам дату в своём формате, который нужно преобразовать в тип DateTime.

Sunday, February 15, 2009 8:07:38 PM (FLE Standard Time, UTC+02:00)   Comments [0]    

 

Тут обнаружился один интересный командлет – ConvertFrom-StringData, который позволяет преобразовывать строку в хэш-таблицы. Скажем, есть файл вида:

ключ1 = значение1
ключ2 = значение2
ключ3 = значение3
ключ4 = значение4

при этом иногда очень хочется работать с этими строками как с объектами. Т.е. при указании объекта и его ключа, например, $a.key1 получить его значение. Как это делается в хэш-таблицах. Вот пример:

[vPodans] $a = @{"key1"="value1";"key2"="value2"} [vPodans] $a Name Value ---- ----- key2 value2 key1 value1 [vPodans] $a.key1 value1 [vPodans] $a.key2 value2 [vPodans]

здесь я создал простую хэш-таблицу. Но если у нас есть файл общего вида, ключ = значение, то его можно легко привести в вид хэш-таблиц:

[vPodans] $a = "key1 = value1" [vPodans] $a key1 = value1 [vPodans] $a.GetType().FullName System.String [vPodans] $b = ConvertFrom-StringData -StringData $a [vPodans] $b Name Value ---- ----- key1 value1 [vPodans] $b.GetType().FullName System.Collections.Hashtable [vPodans] $b.key1 value1 [vPodans]

сперва я создал строку, которая состоит из пары ключ = значение, в чём мы можем убедиться в типе данных. Вторым этапом я сконвертировал эту строку в хэш-таблицу. Однако, следует учесть, что такое возможно только для строки, но не массива строк. Вот так легко попасть в засаду:

[vPodans] $a = gc keys.txt [vPodans] $a key1 = value1 key2 = value2 key3 = value3 key4 = value4 [vPodans] $a[0] key1 = value1 [vPodans] ConvertFrom-StringData -StringData $a ConvertFrom-StringData : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'StringData '. Specified method is not supported. At line:1 char:35 + ConvertFrom-StringData -StringData <<<< $a + CategoryInfo : InvalidArgument: (:) [ConvertFrom-StringData], ParameterBindingException + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.ConvertFromStringDataCommand [vPodans]

командлет Get-Content (или его алиас GC, не путать с глобальным каталогом) читает файл построчно в виде массива. Следовательно каждая строка является отдельным элементом массива. Как быть в такой ситуации? На первый взгляд может показаться, что можно Get-Content разобрать через Foreach-Object и уже отдельные элементы массива подавать конвертеру в качестве строк. Смотрим:

[vPodans] $a = gc keys.txt | %{ConvertFrom-StringData -StringData $_} [vPodans] $a Name Value ---- ----- key1 value1 key2 value2 key3 value3 key4 value4 [vPodans] $a.key1 [vPodans] $a.GetType().FullName System.Object[] [vPodans] $a[0].key1 value1 [vPodans]

Как видите, мы на выходе получили не одну хэш-таблицу, а массив одиночных хэш-таблиц и для доступа к его ключам и значениям нужно ещё указывать номер элемента в массиве. Чтобы решить данную проблему нужно каким-то образом прочитать файл не построчно, а в виде целой строки. Для чтения файла целиком можно воспользоваться методом ReadAllText класса File Class. Как видно из описания метода, он читает текст в единую строку. Давайте посмотрим, что у нас получится:

[vPodans] $a = [io.file]::ReadAllText("keys.txt") [vPodans] $a key1 = value1 key2 = value2 key3 = value3 key4 = value4 [vPodans] $a.GetType().FullName System.String [vPodans] $b = ConvertFrom-StringData -StringData $a [vPodans] $b Name Value ---- ----- key2 value2 key4 value4 key1 value1 key3 value3 [vPodans] $b.key1 value1 [vPodans] $b.key2 value2 [vPodans] $b.key3 value3 [vPodans] $b.key4 value4 [vPodans]

или просто в одну строчку:

ConvertFrom-StringData -StringData ([io.file]::ReadAllText("keys.txt"))

Вот так просто в стиле The PowerShell Way (в одну строчку) можно текстовые файлы сконвертировать в хэш-таблицы и работать с ними как с объектами, что есть удобно и полезно.

Tuesday, February 10, 2009 10:43:49 AM (FLE Standard Time, UTC+02:00)   Comments [4]    

 

Давненько я ничего не постил в блог. Причины разные, это и проблема отыскать интересный материал для блога и лень. Но сегодня нашёл что написать. Это портированный скрипт с VBS для извлечения установочного ключа Windows и других продуктов, как Microsoft Office.

итак, исходный вариант на VBS, который был найден на просторах интернета:

Set WshShell = WScript.CreateObject("WScript.Shell")
strDigitalProductId="HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DigitalProductId"
strXPKey=GetKey(WshShell.RegRead(strDigitalProductId))
MsgBox "Key:"&strXPKey
Function GetKey(rpk)
    Const rpkOffset=52:i=28
    szPossibleChars="BCDFGHJKMPQRTVWXY2346789"
    Do 'Rep1
        dwAccumulator=0 : j=14
        Do 
            dwAccumulator=dwAccumulator*256 
            dwAccumulator=rpk(j+rpkOffset)+dwAccumulator
            rpk(j+rpkOffset)=(dwAccumulator\24) and 255
            dwAccumulator=dwAccumulator Mod 24
            j=j-1
        Loop While j>=0
    i=i-1 : szProductKey=mid(szPossibleChars,dwAccumulator+1,1)&szProductKey
    if (((29-i) Mod 6)=0) and (i<>-1) then 
    i=i-1 : szProductKey="-"&szProductKey
    End If
    Loop While i>=0 'Goto Rep1
GetKey=szProductKey
End Function

Учитывая мои почти нулевые познания в VBS пришлось сильно повозиться, чтобы понять его работу в VBS и активно сотрудничать с гуглом.

И столкнулся с некоторыми трудностями:

rpk(j+rpkOffset)=(dwAccumulator\24) and 255

вот здесь происходит деление. Но не простое деление, а деление с получением целого числа от частного с отрезанием десятичной части без округления. Возможно в VBS правила округления стандартные, но в PowerShell округление происходит несколько иначе, если у нас число с половинкой, например: 1,5; 5,5; 67,5. В таких случаях PowerShell округляет число до ближайшего чётного числа. Примеры:

[vPodans] [math]::Round(0.5) 0 [vPodans] [math]::Round(1.5) 2 [vPodans] [math]::Round(2.5) 2 [vPodans] [math]::Round(3.5) 4 [vPodans] [math]::Round(4.5) 4 [vPodans] [math]::Round(5.5) 6 [vPodans] [math]::Round(6.5) 6 [vPodans] [math]::Round(7.5) 8 [vPodans] [math]::Round(8.5) 8 [vPodans]

Как видите с использованием математической функции округления вы никогда не получите нечётное число, если округлять такие числа. Есть ещё вариант форматирования строки. Но он при отбрасывании дробной части округляет половинки до бОльшего целого числа (хотя нужно до меньшего, что и делает VBS в данном случае):

[vPodans] (0.5).tostring("F00") 1 [vPodans] (1.5).tostring("F00") 2 [vPodans] (2.5).tostring("F00") 3 [vPodans] (3.5).tostring("F00") 4 [vPodans] (4.5).tostring("F00") 5 [vPodans] (5.5).tostring("F00") 6 [vPodans]

ну и я не нашёл ничего лучше, как написать простенький регексп и им отрезать ненужный хвостик. Суть регекспа будет заключаться в извлечении из строки только цифр начиная от начала строки до первого нечислового символа. Как я уже писал в предыдущем блоге в регулярных выражениях начало строки обозначается знаком возведения в степень – ^. Только числа - \d. Т.к. число может состоять из множества цифр, то я добавил символ умножения – *, чтобы сказать: от нуля и более символов. Получилось вот так:

[vPodans] 3456.356345 -match "^\d*" True [vPodans] $matches Name Value ---- ----- 0 3456 [vPodans] $matches[0] 3456 [vPodans]

я применил свой шаблон к произвольному дробному числу и оно попало под шаблон. Используя особую переменную $matches, которая в виде массива хранит все совпадения оператора –match, я могу извлекать ту часть сроки, которая попала под шаблон. Т.к. $matches у нас массив, то для вставки совпавшей части в код я буду обращаться к нужному элементу этого массива. В моём случае совпадение только одно, поэтому $matches будет массивом из единственного элемента и обращаться к его значению нужно по первому индексу, т.е. $matches[0].

Далее, мы видим оператор And. Здесь я тоже попал в засаду, поскольку подумал, что это аналог –AND в PowerShell. Однако после чтения документации выяснилось, что это Bitwise AND, т.е. побитовый “И”. Следовательно при портировании необходимо использовать не –AND, а –BAND. Разница у них в том, что оба числа преобразовываются в двоичный формат и побитово выполняется операция логического умножения. Подробности здесь: http://msdn.microsoft.com/en-us/library/z0zec0b2(VS.71).aspx.

szProductKey=mid(szPossibleChars,dwAccumulator+1,1)&szProductKey

оператор MID в VBS (как и в древнем досовском бейсике) вырезает часть строки и является аналогом метода Substring(), который есть в PowerShell. Однако тут я жестоко попался на следующее: в PowerShell любая нумерация индексов и символов начиная с нуля. Т.е. чтобы извлечь только первый символ из строки мне нужно указать стартовый индекс ноль - 0:

[vPodans] "here string".substring(0,1) h [vPodans]

но в VBS символы в строках нумеруются начиная с единицы – 1. Те. для моего примера чтобы вырезать первый символ строки в качестве стартовой позиции нужно было указывать не 0, а 1:

string = "here string"
msgbox mid(string,1,1)

Следовательно при портировании скрипта из вышеупомянутой строки убираем  “+1”, который, видимо, был добавлен ввиду особенности нумерации символов строки в VBS. Для кого-то это может показаться пустяком, но для меня это всё было открытием, поскольку я на VBS не написал ни одного скрипта (даже который просто мапит шару при логоне) и его синтаксис для меня достаточно сложен и не всегда понятен.

В остальном же я ограничился построчной трансляцией кода без каких-либо изменений с использованием аналогов PowerShell, поскольку я до конца и не разобрал алгоритм шифрования ключа. На выходе у меня получился вот такой скрипт:

########################################################
# Get-ProductKey.ps1
# Version 1.0
#
# Retrieve Windows XP/2003/Vista/2008 setup key
#
# Note: not works on Windows Vista/2008 activated by KMS Server # Note: translated from VBS script
# #
Vadims Podans (c) 2009 # http://www.sysadmins.lv/ ######################################################## function Get-ProductKey { $rpk = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\" -Name DigitalProductId).DigitalProductId $i = 28 $rpkOffset = 52 $PossibleChars = "BCDFGHJKMPQRTVWXY2346789" do { $Accumulator = 0 $j = 14 do { $Accumulator = $Accumulator * 256 $Accumulator = $rpk[$j + $rpkOffset] + $Accumulator $Accumulator / 24 -match "^\d*" | Out-Null $rpk[$j + $rpkOffset] = $matches[0] -band 255 $Accumulator = $Accumulator % 24 $j-- } while ($j -ge 0) $i-- $ProductKey = $PossibleChars.Substring($Accumulator, 1) + $ProductKey if ((29 - $i) % 6 -eq 0 -and $i -ne -1) { $i-- $ProductKey = "-" + $ProductKey } } while ($i -ge 0) $ProductKey }

Примечание: Данный скрипт не будет возвращать установочный ключ для VLK систем Windows Vista и Windows Server 2008, которые были активированы через KMS сервер. Здесь ключ будет показан только для OEM и коробочных версий. На сколько я знаю в данном случае (как у меня на нотебуке с Vista Business) при активации через KMS ключ не хранится в системе и скрипт будет возвращать буквы B:

[vPodans] Get-ProductKey BBBBB-BBBBB-BBBBB-BBBBB-BBBBB [vPodans]

объяснение этому здесь: http://www.rjlsoftware.com/support/faq/sa.cfm?q=289&n=81

з.ы. ссылка не показывается в IE8 RC1, только верхний баннер :'( Поэтому пришлось совершить грех и воспользоваться мозиллой :)

Так же можно получать установочные ключи не только для самой ОС, но и для приложений тоже (разработки Microsoft, разумеется), как Microsoft Office. Для этого достаточно изменить ветку реестра на:

HKLM\Software\Microsoft\Office\11.0\Registration\{GUID}\DigitalProductID

Этот путь будет справедлив для Office 2003. Где хранит этот ProductID Office 2007 я не нашёл у себя. Но вообще мысль понятна, в каком направлении искать :)

Удачи!

Thursday, February 05, 2009 7:31:53 PM (FLE Standard Time, UTC+02:00)   Comments [1]    

 

 · 

All content © 2008 - 2012, Vadims Podāns
"Spaces" Theme provided by: Vadims Podāns
About


E-mail - Send mail to the author(s)
Live Messenger -
For english language visitors
Библиотека
Календарик
<February 2012>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910

Карта расположения посетителей
Favorites





Disclaimer
Вся информация на сайте предоставляется на условиях «как есть», без предоставления каких-либо гарантий и прав.

При использовании материалов c данного сайта ссылка на оригинальный источник обязательна.
Protected by Copyscape Online Plagiarism Scanner