注意
自己責任でお願いします。これら記事の内容を実行して何か不都合を被ったとしても筆者は責任を負いません。
性質上セキュリティホールを自分で開けているようなものなので、実装は実験目的かつ仮想環境を推奨します。また当たり前ですが実装自体には管理者権限が必要です。また自分の所有する端末のみで行ってください。
もし sudo を使いたいなら、Windows 標準 か Grignoli 氏の gsudo を使えばよいと思います。
構成
目次
1. 変数の宣言と代入
2. 接続設定
3. 構成ファイル作成・登録
4. 権限設定
5. 証明書作成
6. 証明書紐付け
7. 実行・関数作成
8. 後始末
説明
- ロール構成:
- 選択したユーザーがリモートでできることを規定する(ホワイトリスト形式)。
- セッション構成:
- 選択したユーザーがリモートで、どのアカウントの権限を用いるのかを決める。またロール構成を指定して、このセッション構成を用いたリモート接続で何ができるかも示す。
- エンドポイント:
- 登録することで、作成したセッション構成での接続が利用可能になる。
内容
使用する変数:
$RoleName $ModulePath $ManifestPd $SessionCfg $RoleInPath $RoleConfig $PhrasePath $Enrollment $UserName $LogonName
$RoleName = "Mock"
$ModDirName = "JEA"
$EntirePath = ($Env:PSModulePath -split ";" -like "$Env:ProgramFiles*")[-1]
$ModulePath = Join-Path $EntirePath $ModDirName
$ManifestPd = Join-Path $ModulePath "${ModDirName}.psd1"
$SessionCfg = Join-Path $ModulePath "${RoleName}Session.pssc"
$RoleInPath = Join-Path $ModulePath RoleCapabilities
$RoleConfig = Join-Path $RoleInPath "${RoleName}.psrc"
$PhrasePath = Join-Path $RoleInPath "phrase.uid"
$Enrollment = $RoleName + "Endpoint"
$SelectUser = [Security.Principal.WindowsIdentity]::GetCurrent().Name
$UserObject = [Security.Principal.NTAccount]::new($SelectUser)
$UserName = $UserObject.Value.Split("\")[-1]
$LogonName = (Get-CimInstance Win32_UserAccount).Where{$_.Name -eq $UserName}[0].Caption
↓すべて管理者権限で実行します↓
準備
# 2 つのディレクトリ $ModulePath と $RoleInPath を作成
mkdir $RoleInPath -Force > $null
# テンプレートなモジュールマニフェストを $ModulePath に作成
New-ModuleManifest $ManifestPd
# コマンド実行をフレーズで制限する
$PhraseGUID = [Guid]::NewGuid().Guid
# フレーズをファイルに保存
New-Item $PhrasePath -Value $PhraseGUID -Force | Out-Null
役割(Role)の定義ファイルを作成
# 許可するコマンドを定義
$Mock = [ScriptBlock]::Create(@'
param(
[string]$__Mock_WorkFolder__,
[switch]$__Mock_ScriptBlock__,
[string]$__Mock_Phrase__,
[object]$__Mock_Command__,
[object]$__Mock_Pipeline__,
[object]$__Mock_Arguments__
)
process {
'@ + "`n" + @"
if ((Get-Content '$PhrasePath' -Encoding UTF8) -ne `$__Mock_Phrase__) {
Write-Error "パラメーター 'Phrase' の引数が一致しません。"
return
}
"@ + "`n" + @'
if (-not $PSBoundParameters.ContainsKey("__Mock_Command__") -or $null -eq $__Mock_Command__) {
Write-Error "パラメーター 'Command' の引数を確認できません。"
return
}
Set-Location -Path $__Mock_WorkFolder__
if (-not ((Get-Location).Provider.Name -eq "FileSystem")) {
Write-Error "現在の場所が FileSystem Provider ではありません。"
return
}
$HasPipeline = -not ($null -eq $__Mock_Pipeline__)
$Arguments = $__Mock_Arguments__.Args
if ($__Mock_ScriptBlock__ -and $HasPipeline) {
$__Mock_Pipeline__ | & ([ScriptBlock]::Create($__Mock_Command__)) @Arguments
return
}
if ($__Mock_ScriptBlock__) {
& ([ScriptBlock]::Create($__Mock_Command__)) @Arguments
return
}
if ($HasPipeline) {
$__Mock_Pipeline__ | & $__Mock_Command__ @Arguments
return
}
& $__Mock_Command__ @Arguments
return
}
'@)
# 役割を定義
$RoleDetail = @{
Path = $RoleConfig
Author = $UserName
Description = "Mock"
VisibleFunctions = @("Mock")
FunctionDefinitions = @( # FullLanguage のコンテキストで実行される
@{ # PoC (実験目的)
Name = "Mock"
ScriptBlock = $Mock
}
)
VisibleCmdlets = @(
"Get-Location"
)
VisibleProviders = "FileSystem"
}
# ファイルとして保存(上書きする)
New-PSRoleCapabilityFile @RoleDetail
セッション構成(Endpoint)の作成と登録
# 内容を構成
$SessionDetail = @{
Path = $SessionCfg
Author = $UserName
SessionType = "RestrictedRemoteServer"
LanguageMode = "NoLanguage" # RestrictedRemoteServer の既定値
RoleDefinitions = @{
$LogonName = @{
RoleCapabilities = $RoleName
}
}
# ExecutionPolicy = "RemoteSigned" # スクリプトを実行したい場合
# EnvironmentVariables = @{ PSModulePath = "" } # 仮想アカウントでは Documents のモジュールパスが存在しないため注意
RunAsVirtualAccount = $true
RunAsVirtualAccountGroups = "Administrators" # 既定
}
# ファイルとして保存
New-PSSessionConfigurationFile @SessionDetail
# 作成したファイルを検証
Test-PSSessionConfigurationFile $SessionCfg
# エンドポイントの登録
Register-PSSessionConfiguration -Name $Enrollment -Path $SessionCfg -Force
確認
# ロールファイル
Get-Content $RoleConfig
# セッションファイル
Get-Content $SessionCfg
# エンドポイント
Get-PSSessionConfiguration $Enrollment