
나쁜 사람들이 만든 나쁜 것들
이 문서에서 분석 및 참조된 Hive 샘플은 @rivitna님이 작성한 이 목록에서 무작위로 선택되었으며, 이 자리에서 가장 따뜻한 감사를 드립니다. 아티팩트는 VirusTotal 플랫폼에서 확인할 수 있습니다.
이 문서에서는 a0h2uih3d2.exe 파일을 참조로 사용했습니다.
MD5: 15CF5E0DA094ACDD751A513402A8C941
SHA-1: 72E15AC4473903C814E65E3C06F54EB0399580AA
SHA-256: 335D2E4A743D059955760ECF2EC25EE86D36AA60B096C9180E860C64EF78EE55
이 랜섬웨어의 복잡성을 파악하려면 Microsoft Threat Intelligence Center(MSTIC)가 발행한 이 분석을 살펴보시기 바랍니다.
코드를 사용하기 전에 문서 전체를 주의 깊게 읽으시기 바랍니다!
최근 몇 달 동안 저는 Hive v5 암호화 알고리즘의 연구와 리버스 엔지니어링에 대부분의 힘을 쏟았습니다. 과거에 Hive의 이전 버전을 분석하고 그 암호화 메커니즘에 관한 코드와 PoC를 공개한 훌륭한 악성코드 분석가이자 리버스 엔지니어인 @rivitna님과 협업할 수 있는 기쁨을 누렸습니다. 그분은 RUST로 작성되어 분석이 더욱 어려워진 Hive v5의 암호화 작업에 관여하는 구성 요소를 식별하는 데 (적지 않게) 기여했습니다. 저는 2021년 6월에 소스가 공개된 또 다른 매우 중요한 랜섬웨어인 Babuk과 공통점을 발견했습니다:
피해자 시스템에서 실행되는 Hive 랜섬웨어 v5는 아래 증거의 알고리즘을 사용하여 QueryPerformanceCounter 및 QueryPerformanceFrequency Windows API를 기반으로 두 개의 평문 키(cleartext key)를 생성합니다.
QueryPerformanceCounter API에 대한 자세한 내용은 이 Microsoft 페이지를, QueryPerformanceFrequency에 대한 내용은 여기를 참조하시기 바랍니다.
QueryPerformanceCounter는 매우 정확한 시간 카운터입니다. 호출되면 PC가 마지막으로 켜진 이후 경과된 시간을 반환합니다.
QueryPerformanceFrequency는 성능 카운터의 값(주파수)을 반환합니다. 고정 값은 0x989680입니다. 즉, QueryPerformanceCounter 값은 초당 0x989680회, 즉 10,000,000회 업데이트된다는 뜻입니다.
두 개의 평문 키는 크기가 0xCFFF00바이트이며 한 번에 한 바이트씩 생성됩니다. 아래는 Hive가 피해자 PC의 파일을 암호화하는 데 사용하는 소위 평문 키(cleartext key) 의 가장 큰 부분인 0xA00000바이트 배열을 생성하는 스니펫입니다.

키의 각 바이트는 AL 레지스터의 값을 취하여 얻습니다. EAX 레지스터에는 createByte라는 라벨로 이름이 변경된 0044ADE0 함수의 결과가 포함되어 있으며, 이 함수는 call_to_QueryPerformanceCounter라는 라벨로 이름이 변경된 함수 0044A850의 첫 번째 호출 시 계산된 초기 시드 값과 현재 시점의 차이를 구현합니다.
다음은 평문 키를 생성하기 위해 C++로 작성된 코드입니다:

알고리즘은 매우 간단하지만, 0044ADE0 함수 내부에는 평문 키 생성 중 코드 실행 시간을 지연시키기 위해 중복 연산과 다양한 조건부 점프를 수행하는 명령어가 삽입되어 있습니다:

HiveRansomwareV5_custom_keygen_PoC 폴더에는 분석된 Hive v5 샘플에서 리버스한 원시 코드가 있습니다. 컴파일된 버전의 코드 한 줄도 놓치지 않아야 했기 때문에 악성코드에서 발견되는 것과 같은 최적화된 코드가 아닙니다.
HiveRansomwareV5_custom_keygen_PoC-optimized 폴더에는 위에서 언급한 원시 코드에서 파생된 최적화된 코드가 있습니다. 이 버전에서는 구현된 기능을 이해할 수 있도록 코드가 원시 코드보다 훨씬 읽기 쉽습니다.
두 버전 모두 실행 전에 사용자 이름으로 커스터마이즈해야 생성된 평문 키를 바탕 화면에 저장할 수 있습니다.
두 평문 키 모두 동일한 알고리즘을 사용하여 생성됩니다.
평문 키는 0xA00000 안전하게 무작위 생성된 바이트로 구성됩니다. 그런 다음 처음 0x2FFF00바이트를 끝에 복사하여 최종 0xCFFF00바이트 평문 키를 생성합니다.

그런 다음 Hive는 생성된 두 개의 키를 사용하여 파일을 암호화하지만, 먼저 Hive 랜섬웨어 v5는 생성된 키를 사용자 지정 구조체(이하 키스트림(keystreams) 이라고 함)로 암호화하여 .key 확장자를 사용해 암호화하는 각 드라이브의 루트에 배치합니다. 예를 들어 시스템에 C와 D 드라이브가 모두 설치되어 있다면 암호화된 키스트림이 각 드라이브의 루트에 존재하게 됩니다.

Hive 랜섬웨어 v5는 생성된 평문 키를 사용하여 XOR 명령어로 파일을 암호화하므로, 최신 x86/x64 CPU에서 매우 빠른 대칭 암호화에 해당합니다.
Hive 랜섬웨어 v5는 생성된 평문 키를 두 번 암호화하여 보호해야 하며, 이후 이를 라운드(rounds) 라고 부르겠습니다. 최종 키스트림을 얻으려면 두 번의 암호화 라운드가 필요합니다.
이를 위해 각 라운드에서 다음 단계가 수행됩니다:
3단계는 암호화 중 Hive가 생성한 개인 키 쌍과 Hive 제휴 파트너가 우리를 위해 랜섬웨어를 컴파일할 때 생성한 개인 키 쌍, 두 쌍의 개인 키로 열 수 있는 키스트림의 생성을 보장합니다.

이 설명의 끝에서 한 가지 특정 지점이 분명해집니다: 두 라운드의 암호화에 사용되는 평문 키, 개인 키 및 논스는 모두 위의 동일한 함수(0044ADE0, 일명 createByte)에 의해 생성됩니다. 함수 0044ADE0은 CPU가 for 루프 내에서 호출되는 코드를 실행하는 데 걸리는 시간에 의해 조건이 결정됩니다.
두 라운드의 암호화 이후 키스트림 구조를 강조한 위 그림을 살펴보면, 우리가 자유롭게 접근할 수 있는 것은 논스뿐이라는 것이 분명합니다 (그렇지 않으면 Hive 제휴 파트너는 파일을 해독하는 방법을 알 수 없을 것입니다).
그러니 24바이트 길이의 논스에 초점을 맞춰 보겠습니다:
NONCE: 40 A4 08 6C D0 D0 34 98 FC 60 C4 28 8C F0 F0 54 B8 1C 80 E4 48 AC AC 10
논스의 한 바이트와 다음 바이트 사이의 차이(절대값)는 한 반복과 다음 반복 사이에 경과된 시간을 나타냅니다. 이 정의로 핑거프린트(fingerprint)라는 개념을 소개합니다.
NONCE FINGERPRINT: 64 9c 64 64 00 9c 64 64 9c 64 9c 64 64 00 9c 64 9c 64 64 9c 64 00 9c
얻은 값을 분석하면 코드의 실행 시간이 거의 동일하며, 특히 사용 중인 프로세서의 기술로 인한 약간의 변동만 있음을 알 수 있습니다. (제 테스트에서는 10세대 i7 프로세서와 5세대 i5를 사용했으며, 다른 시스템에서는 이 핑거프린트가 다를 수 있습니다.)
논스가 평문 키, 그리고 무엇보다 개인 키를 생성하는 동일한 함수에 의해 생성된다는 점을 고려하면 이 발견은 매우 중요합니다. 언급된 이러한 값들도 이 원칙을 따르기 때문에, 즉 논스의 개별 바이트 간 차이는 예측 가능하므로 개인 키와 평문 키 값도 예측 가능할 것입니다.
그러나 분석 결과 Hive가 계산한 원래 평문 키와 동일한 바이트를 얻기 위해 0xA00000 문자 배열을 생성하는 것은 매우 어렵다는 것이 입증되었습니다: CPU 및 메모리 부하의 변동은 코드 실행 속도에 영향을 미치며, Hive PE가 계산한 원래 평문 키는 우리가 계산한 키와 (단 몇 바이트라도) 다른 경우가 많습니다.
우리는 이 논스 핑거프린트를 0xA00000바이트 길이의 가능한 사전 생성에서 얻은 핑거프린트와 비교하는 데 사용합니다(이 숫자는 경험적으로 고정되었으며, 일련의 테스트 후 통계적으로 이 바이트 수 안에 두 라운드의 암호화에 필요한 각각 32바이트인 두 개의 개인 키가 있다는 것이 확인되었습니다). 논스 핑거프린트가 사전 핑거프린트에 포함되어 있다면 두 개인 키를 모두 브루트포스하기 위한 올바른 사전을 찾은 것입니다.
여기서 끝나지 않습니다. 논스, 평문 키 및 개인 키의 생성에 대해 수행된 동적 분석에서 첫 번째 바이트는 거의 균일한 값을 가진 다른 모든 바이트와 비교하여 두 번째 바이트와 평균 거리가 다르다는 것이 검증되었기 때문입니다. 자세히 살펴보겠습니다:

보시다시피 첫 번째 이후의 핑거프린트 값은 최소한의 변동을 겪습니다. 즉, 개인 키의 첫 번째 바이트와 두 번째 바이트 사이의 절대값 거리는 대부분의 경우 나머지 핑거프린트에 존재하는 값 범위를 벗어납니다.
아마도 이는 CPU에 존재하는 일부 최적화 알고리즘 때문이며, for 루프의 첫 번째 반복 이후 코드 실행 속도를 높이는 것과 관련이 있습니다.
제안된 코드는 각 키스트림 암호화 라운드의 논스를 읽고 핑거프린트를 결정하며 가능한 개인 키를 포함하는 가능한 키 사전 목록을 생성합니다.
항상 키의 나머지 부분과 다른 핑거프린트의 첫 번째 바이트와 관련된 문제를 해결하기 위해 다음과 같은 방법을 생각했습니다:
두 공개 키가 일치하면 두 번째(마지막) 암호화 라운드가 암호화된 개인 키를 찾은 것입니다. 지금까지 설명한 작업을 다시 반복하면 첫 번째 라운드의 암호화된 키스트림을 해독할 개인 키를 얻을 수 있으며, 마지막으로 원래 평문 키를 추출할 수 있습니다.
HiveRansomwareV5-keystream_decryptor 폴더에는 VS 2017 sln과 monocypher 맞춤형 라이브러리가 있습니다. 프로그램에서 수행할 작업을 선택할 수 있습니다.

옵션 "1"은 PC 프로세서에 맞춤화된 바이트 사전을 만들 수 있으므로 가장 먼저 선택해야 합니다. 따라서 암호화된 머신에서 수행해야 합니다. 해당 키스트림에 존재하는 값과 동일한 값을 얻을 가능성이 훨씬 높기 때문입니다.

또는 첫 번째 옵션이 작동하지 않으면 (이미 감염된 동일한 PC에서) 디버거에서 악성코드를 평문 키 생성이 끝날 때까지(for 루프 바로 밖) 실행하여 평문 키가 포함된 메모리 내용을 저장하여 자신만의 사전을 생성하십시오. 이 경우 옵션 "3"을 사용하여 사전을 검증할 수 있습니다:

키스트림에 대한 올바른 사전을 확보하면 옵션 "2"는 개인 키 바이트 값에 어떤 식으로든 영향을 주지 않으면서 바이트 조합을 브루트포스하는 데 필요한 시간을 줄일 수 있는 더 강력한 컴퓨터에서도 수행할 수 있습니다.

옵션 2의 기능을 제대로 수행하려면 공개 키를 추출해야 합니다. 모든 Hive 샘플이 동일하게 생성되는 것은 아니므로 범용 공개 키 추출기를 만드는 것은 그리 쉽지 않습니다. 그러나 작동하는 것으로 알려진 한 가지 방법은 논스 생성 후 디스어셈블된 이 부분에 중단점(breakpoint)을 설정하는 것입니다. 아래 증거에서 공개 키는 curve25519를 사용하여 공유 키를 파생하기 위해 함수 44E3D8에 전달됩니다. 악성코드 실행 전체에서 공개 키가 노출되는 유일한 곳입니다.

Visual Studio 옵션을 다룰 때 주의하시기 바랍니다. 디버그에서 릴리스로 또는 그 반대로 전환하면 사전 생성 바이트와 같은 프로그램 출력이 변경될 수 있습니다.
브루트포스 절차의 속도를 높이려면 코드에서 dictionary_dimension 값을 편집할 수 있지만, 사전 크기를 줄이면 개인 키를 찾을 확률도 낮아질 수 있다는 점에 유의하시기 바랍니다.
또한 Hive를 실행하고 메모리에서 덤프하여 생성된 평문 바이트 배열을 사용하기로 결정한 경우 dictionary_dimension 변수에 덤프 크기를 설정하는 것을 잊지 마십시오.
복호화 도구를 테스트하기 위해 dummy_data_PoC 폴더에 사용할 파일 몇 개를 제공했습니다:
행운을 빕니다!
https://github.com/rivitna/Malware/blob/main/Hive/Hive_samples.txt
https://www.virustotal.com/gui/file/335d2e4a743d059955760ecf2ec25ee86d36aa60b096c9180e860c64ef78ee55
https://www.microsoft.com/security/blog/2022/07/05/hive-ransomware-gets-upgrades-in-rust/
https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps
https://monocypher.org/manual/x25519
https://monocypher.org/manual/advanced/poly1305