about_Language_Modes — PowerShell 세션의 언어 모드

about_Language_Modes — PowerShell 세션의 언어 모드

PowerShell 세션에서 어떤 언어 요소를 쓸 수 있고 못 쓰는지를 결정하는 게 바로 언어 모드(Language Mode)예요. 한마디로 "이 세션에서는 이 정도까지 허용된다" 하는데요, 보안이 중요한 환경에서는 이걸 조여서 악성 코드가 돌지 못하게 막는 용도로 자주 써요. 어떤 모드들이 있고, 언제 어떤 모드가 걸리는지, 그리고 각 모드가 정확히 무엇을 허용하는지 하나씩 살펴볼게요.

출처: about_Language_Modes - Microsoft Learn

본문

한 줄 요약

언어 모드는 PowerShell 세션 안에서 그 언어의 어느 요소를 쓸 수 있는지를 결정해요.

언어 모드란 무엇인가요?

언어 모드는 세션에서 허용되는 언어 요소를 정해 주는 속성이에요. 세션을 만들 때 쓰는 세션 구성(session configuration, 줄여서 "endpoint")에 속해 있죠. 그래서 특정 세션 구성을 쓰는 세션은 전부 그 구성의 언어 모드를 그대로 따라가요.

모든 PowerShell 세션에는 언어 모드가 있어요. 세션은 대상 컴퓨터에 있는 세션 구성을 바탕으로 만들어지는데, 그 세션 구성에 설정된 언어 모드가 곧 세션의 언어 모드가 되는 거예요. PSSession의 세션 구성을 직접 지정하고 싶다면, 세션을 만드는 cmdlet에서 ConfigurationName 매개 변수를 쓰면 돼요.

참고로 PowerShell 7.3부터는 pwshConfigurationFile 매개 변수와 함께 실행할 수 있어요. 이렇게 하면 특정 구성을 지정해서 PowerShell을 시작할 수 있죠.

세션의 언어 모드 확인하기

FullLanguage나 ConstrainedLanguage 세션이라면, 세션 상태의 LanguageMode 속성값을 조회해서 언어 모드를 알 수 있어요. 예를 들면 이렇게요.

$ExecutionContext.SessionState.LanguageMode
ConstrainedLanguage

그런데 RestrictedLanguage나 NoLanguage 세션에서는 멤버 접근 연산자(.)를 써서 속성값을 가져올 수 없어요. 이 경우에는 대신 오류 메시지가 언어 모드를 알려 줘요.

RestrictedLanguage 세션에서 $ExecutionContext.SessionState.LanguageMode에 접근하면 PowerShell이 PropertyReferenceNotSupportedInDataSectionVariableReferenceNotSupportedInDataSection 오류 메시지를 반환해요.

  • PropertyReferenceNotSupportedInDataSection: 속성 참조는 제한 언어 모드나 Data 섹션에서는 허용되지 않아요.
  • VariableReferenceNotSupportedInDataSection: 제한 언어 모드나 Data 섹션에서 참조할 수 없는 변수를 참조하고 있어요.

NoLanguage 세션에서 $ExecutionContext.SessionState.LanguageMode에 접근하면 ScriptsNotAllowed 오류 메시지가 나와요.

  • ScriptsNotAllowed: 이 runspace는 해당 구문을 지원하지 않아요. no-language 모드이기 때문일 수 있어요.

세션 구성의 언어 모드 확인하기

세션 구성을 세션 구성 파일로 만들었다면, 그 구성에는 LanguageMode 속성이 있어요. 이 속성값을 조회하면 언어 모드를 확인할 수 있어요.

(Get-PSSessionConfiguration -Name Test).LanguageMode
FullLanguage

그 외의 세션 구성에서는 간접적으로 확인할 수 있어요. 그 구성을 써서 만든 세션의 언어 모드를 찾아보면 되는 거죠.

언어 모드 설정하기

PowerShell 세션의 언어 모드는 내장 변수인 $ExecutionContext를 통해서도 설정할 수 있어요.

$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"

다만 이렇게 직접 바꾸는 방법은 언어 모드를 실험해 볼 때만 유용해요. 언어 모드의 실제 목적은 특정 상황에서 PowerShell 세션의 보안을 높이는 데 있으니까, 평소엔 아래 두 경로로 설정하는 게 정석이에요. 시스템 애플리케이션 제어 정책을 쓰거나, 세션 구성을 만들 때 정하는 거죠.

시스템 애플리케이션 제어 정책 사용하기

시스템 애플리케이션 제어 정책 아래에서 실행되면 PowerShell은 자동으로 ConstrainedLanguage 모드로 돌아가요. 이때 감지되는 애플리케이션 제어 정책은 Windows 플랫폼의 AppLocker와 Windows Defender Application Control(WDAC)이에요.

애플리케이션 제어 정책이 감지되면 PowerShell은 언어 모드 말고도 다른 제한을 추가로 적용해요. 예를 들어 정책 아래에서는 dot-sourcing이나 모듈 가져오기에 추가 제한이 붙어요.

정책 아래에서 시작된 PowerShell 세션은 ConstrainedLanguage 모드로 실행돼요. PowerShell 7.4부터는 시작 배너에 그 모드로 실행 중이라는 메시지가 표시돼요. 이 덕분에 사용자는 cmdlet과 네이티브 명령을 실행하고 기본 언어 요소에 접근하는 등 쓸 만한 대화형 셸 환경을 그대로 누릴 수 있어요. 다만 악의적인 행위자가 악용할 수 있는 PowerShell, .NET, COM API에는 접근할 수 없어요.

이 세션에서 실행되는 스크립트나 스크립트 기반 모듈은 모두 ConstrainedLanguage 모드로 돌아가요. 그런데 정책이 허용한 스크립트나 스크립트 기반 모듈은 아무 제약 없이 FullLanguage 모드로 실행돼요. 이렇게 해서 정책으로 잠근 시스템에서도 정책이 신뢰하는 스크립트는 제약을 거의 받지 않고 돌아갈 수 있는 거죠.

세션 구성 사용하기

PowerShell 원격 기능은 선택적으로 사용자 지정 세션 구성을 지원해요. 그 사용자 지정 구성에 원하는 언어 모드를 설정할 수 있죠. PowerShell Just Enough Administration(JEA) 구성은 NoLanguage 모드를 써서 세션을 명령 호출로만 제한해요. JEA를 쓰면 원격 세션을 특정 사용자로 제한할 수 있고, 그 JEA 사용자는 정의된 명령 집합만 실행할 수 있어요. API나 파일 시스템, 그 밖의 시스템 리소스에는 직접 접근할 수 없고요.

자세한 내용은 JEA Session configurationsNew-PSSessionConfigurationFile을 참고해요.

언어 모드별 특징과 제한

이번엔 PowerShell 세션에서 각 언어 모드가 뭘 허용하고 뭘 막는지 하나씩 살펴볼게요.

FullLanguage 모드

FullLanguage 모드는 세션의 모든 언어 요소를 허용해요. 그리고 모든 Windows 버전의 기본 세션에서 FullLanguage가 기본 언어 모드예요.

RestrictedLanguage 모드

RestrictedLanguage 모드에서는 사용자가 명령(cmdlet, 함수, CIM 명령, workflow)을 실행할 수 있지만, 스크립트블록(scriptblock)은 쓸 수 없어요. 이 모드는 Import-Module이 불러오는 모듈 매니페스트를 처리할 때도 사용돼요.

PowerShell 7.2부터는 시스템 잠금(system lockdown)이 구성된 환경에서 RestrictedLanguage 모드의 New-Object cmdlet이 비활성화돼요.

기본적으로 RestrictedLanguage 모드에서 허용되는 변수는 다음뿐이에요.

  • $PSCulture
  • $PSUICulture
  • $true
  • $false
  • $null

모듈 매니페스트는 RestrictedLanguage 모드로 불러와지며, 여기에 추가 변수가 더 쓰일 수 있어요.

  • $PSScriptRoot
  • $PSEdition
  • $EnabledExperimentalFeatures
  • 환경 변수처럼 생긴 것들, 예를 들면 $env:TEMP

허용되는 비교 연산자도 다음뿐이에요.

  • -eq (equal)
  • -gt (greater-than)
  • -lt (less-than)

할당문, 속성 참조, 메서드 호출은 허용되지 않아요.

ConstrainedLanguage 모드

ConstrainedLanguage 모드는 반복문, 조건문, 문자열 확장, 객체 속성 접근 같은 기본 언어 요소는 허용하도록 설계됐어요. 제한은 악의적인 행위자가 악용할 수 있는 작업을 막는 쪽으로 걸려 있죠.

ConstrainedLanguage 모드는 모든 cmdlet과 PowerShell 언어 요소의 일부를 허용하되, 사용할 수 있는 객체 유형을 제한해요. 이 모드의 특징을 정리하면 이래요.

  • Windows 모듈의 모든 cmdlet은 언급된 예외를 빼면 완전히 동작하고 시스템 리소스에 완전히 접근할 수 있어요.
  • PowerShell 스크립팅 언어의 모든 요소가 허용돼요.
  • Windows에 포함된 모든 모듈을 가져올 수 있고, 모듈이 내보내는 모든 명령을 세션에서 실행할 수 있어요.
  • Add-Type cmdlet은 서명된 어셈블리는 불러올 수 있지만, 임의의 C# 코드나 Win32 API는 불러올 수 없어요.
  • New-Object cmdlet은 허용된 유형(아래 목록)에만 사용할 수 있어요.
  • PowerShell에서 사용할 수 있는 유형은 허용된 유형뿐이에요. 그 외 유형은 허용되지 않아요. 형식 변환은 허용되지만 결과가 허용된 유형일 때만 가능해요.
  • 문자열 입력을 유형으로 변환하는 cmdlet 매개 변수도 결과가 허용된 유형일 때만 동작해요.
  • 허용된 유형의 ToString() 메서드와 .NET 메서드는 호출할 수 있어요.
  • 사용자는 허용된 유형의 모든 속성을 가져올 수 있어요. 속성값을 설정할 수 있는 건 허용된 유형뿐이에요.

중요 — ConstrainedLanguage 모드가 우회되지 않도록 하려면 App Control for Business의 System Lockdown 모드에서 반드시 ConstrainedLanguage 모드를 써야 해요. PowerShell이 AppLocker와 App Control을 어떻게 지원하는지 자세히 보려면 Use App Control to secure PowerShell을 참고해요.

ConstrainedLanguage 모드에서 허용되는 .NET 유형은 다음과 같아요. 사용자는 이 유형들의 속성을 가져오고, 메서드를 호출하고, 객체를 이 유형들로 변환할 수 있어요.

허용되는 유형:

  • [adsi] (Windows 전용)
  • [adsisearcher] (Windows 전용)
  • [Alias]
  • [AllowEmptyCollection]
  • [AllowEmptyString]
  • [AllowNull]
  • [ArgumentCompleter]
  • [ArgumentCompletions]
  • [array]
  • [bigint]
  • [bool]
  • [byte]
  • [char]
  • [cimclass]
  • [cimconverter]
  • [ciminstance]
  • [CimSession]
  • [cimtype]
  • [CmdletBinding]
  • [cultureinfo]
  • [datetime]
  • [decimal]
  • [double]
  • [DscLocalConfigurationManager]
  • [DscProperty]
  • [DscResource]
  • [ExperimentAction]
  • [Experimental]
  • [ExperimentalFeature]
  • [float]
  • [guid]
  • [hashtable]
  • [int]
  • [int16]
  • [int32]
  • [int64]
  • [ipaddress]
  • [IPEndpoint]
  • [long]
  • [mailaddress]
  • [Microsoft.PowerShell.Commands.ModuleSpecification]
  • [NoRunspaceAffinity]
  • [NullString]
  • [Object[]]
  • [ObjectSecurity]
  • [ordered]
  • [OutputType]
  • [Parameter]
  • [PhysicalAddress]
  • [pscredential]
  • [pscustomobject]
  • [PSDefaultValue]
  • [pslistmodifier]
  • [psobject]
  • [psprimitivedictionary]
  • [PSTypeNameAttribute]
  • [regex]
  • [sbyte]
  • [securestring]
  • [semver]
  • [short]
  • [single]
  • [string]
  • [SupportsWildcards]
  • [switch]
  • [timespan]
  • [uint]
  • [uint16]
  • [uint32]
  • [uint64]
  • [ulong]
  • [uri]
  • [ushort]
  • [ValidateCount]
  • [ValidateDrive]
  • [ValidateLength]
  • [ValidateNotNull]
  • [ValidateNotNullOrEmpty]
  • [ValidateNotNullOrWhiteSpace]
  • [ValidatePattern]
  • [ValidateRange]
  • [ValidateScript]
  • [ValidateSet]
  • [ValidateTrustedData]
  • [ValidateUserDrive]
  • [version]
  • [void]
  • [WildcardPattern]
  • [wmi] (Windows 전용)
  • [wmiclass] (Windows 전용)
  • [wmisearcher] (Windows 전용)
  • [X500DistinguishedName]
  • [X509Certificate]
  • [xml]

허용되는 COM 객체 유형은 다음뿐이에요.

  • Scripting.Dictionary
  • Scripting.FileSystemObject
  • VBScript.RegExp

특수한 경우:

  • [ref] — 객체를 [ref][Management.Automation.PSReference] 유형으로 캐스팅하는 것은 허용되지 않아요. 그 밖의 용도는 허용돼요.

NoLanguage 모드

NoLanguage 모드는 PowerShell 스크립팅 언어를 완전히 비활성화해요. 스크립트를 실행할 수도, 변수를 쓸 수도 없고, 네이티브 명령과 cmdlet만 실행할 수 있어요.

PowerShell 7.2부터는 시스템 잠금이 구성된 환경에서 NoLanguage 모드의 New-Object cmdlet이 비활성화돼요.

더 알아보기