⚠ Spoiler: Đây là write-up cho các challenge của Flare-on 9 tổ chức vào khoảng tháng 11/2022 tại Website.
[09] encryptor
You're really crushing it to get this far. This is probably the end for you. Better luck next year!
7-zip password: flare
Công cụ sử dụng:
- Visual Studio (C++)
- Python
- CFF Explorer
Gần tương tự với challenge 05, ta có 2 file
- flareon.exe
- SuspiciousFile.txt.Encrypted
Main của chương trình tại địa chỉ 403BF0
cho biết cách sử dụng chương trình là thực thi với tham số đầu vào là (các) file có phần mở rộng .EncryptMe
. Mỗi file sẽ được xử lý mã hóa bởi 4022A3
Chương trình sử dụng SystemFunction036
undocument trong dll advapi32.dll
dẫn đến mỗi lần chạy đối với cùng file cho ra kết quả khác nhau.
No Random
Để loại bỏ các kết quả khác nhau giữa các lần chạy thì ta có thể chuyển hướng SystemFunction036
sang file dll khác bằng cách patch file PE → import từ bdvapi32.dll
thay vì advapi32.dll
#include "pch.h"
#include <cstdlib>
#define DLLEXPORT extern "C" __declspec(dllexport)
#pragma warning(disable:4996)
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
srand(0);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
DLLEXPORT bool __stdcall HelloWorld()
{
return MessageBox(NULL, TEXT("Hello World"),
TEXT("In a DLL"), MB_OK);
}
DLLEXPORT BOOLEAN __stdcall SystemFunction036(PVOID RandomBuffer, ULONG RandomBufferLength)
{
for (ULONG i = 0; i < RandomBufferLength; i++) {
*((char*)RandomBuffer+i) = rand()&0xFF;
}
return TRUE;
}
Dựa theo mô tả của challenge thì có khả năng mã hóa thực hiện bởi kết hợp của mã hóa đối xứng (có nhiều khả năng - RC4, TEA, XTEA) và bất đối xứng (RSA, ECC)
sub_4021D0:
__int64 seed()
{
__int64 p[17]; // [rsp+30h] [rbp-348h] BYREF
__int64 q[17]; // [rsp+B8h] [rbp-2C0h] BYREF
__int64 pmin_1[17]; // [rsp+140h] [rbp-238h] BYREF
__int64 qmin_1[17]; // [rsp+1C8h] [rbp-1B0h] BYREF
__int64 phi_n[17]; // [rsp+250h] [rbp-128h] BYREF
__int64 d[17]; // [rsp+2D8h] [rbp-A0h] BYREF
do
getprime(p);
while ( !(unsigned int)checkprime((unsigned __int64 *)p) );// generate prime
do
getprime(q);
while ( !(unsigned int)checkprime((unsigned __int64 *)q) );
multiply(numberN, (unsigned __int64 *)p, (unsigned __int64 *)q);
dec((unsigned __int64 *)pmin_1, (unsigned __int64 *)p);
dec((unsigned __int64 *)qmin_1, (unsigned __int64 *)q);
multiply(phi_n, (unsigned __int64 *)pmin_1, (unsigned __int64 *)qmin_1);
find_d(numberE, numberE, phi_n); // d
return powmod_encryptasymetric_sub_4016CC(numberd_crypted, (DWORD *)d, &numberE2, &numberNford);
}
- khởi tạo 2 số nguyên tố dựa theo bảng số nguyên tố ở
unk_405160
sử dụng phép tính cộng số lớnsub_401992
: p, q - sub_401550 nhân hai số lớn
sub_401550
: p*q → n - sub_401550 nhân số lớn (p-1)*(q-1) → phi
- sub_401B46 ????
Nếu bạn quen với cryptography thì có thể nhận ra thuật toán trong sub_4021D0
tương đồng với mã hóa sử dụng RSA (bất đối xứng) do đó có nhiều khả năng sub_401B46 sẽ tìm ra d sao cho
e*d ≡ 1 mod phi
sub_4020F0
thực hiện mã hóa chacha20 (đối xứng) dùng key và nonce được tạo ngẫu nhiên bởi SystemFunction036
Đối với chương trình ransomware sử dụng kết hợp mã hóa đối xứng (AES) và bất đối xứng (RSA) trải qua các bước:
- Tạo key AES - tốc độ xử lý nhanh, không bảo mật nếu key hardcoded
- Mã hóa key AES sử dụng RSA - tốc độ chậm, bảo mật với e & n hardcoded
Ở phía decryptor
- Giải mã key AES sử dụng RSA → key = F(key_encrypted, n, d), d private
Gần giống như ransomware, chương trình flareon.exe thực hiện:
SystemFunction036(key, 0x20u); // key
SystemFunction036(&nonce[1], 0xCu); // nonce
encryptChacha20(outf, inf, (__int128 *)key, (__int128 *)nonce);
powmod_encryptasymetric_sub_4016CC(cryptedkey, (DWORD *)key, numberE, numberN);// 8
- Tạo key chacha20
- Mã hóa key chacha20 sử dụng RSA sử dụng key n_runtime, E
- Mã hóa khóa d sử dụng RSA với key n_for_d hardcoded, E2
Ở phía decryptor:
- Giải mã khóa d sử dụng RSA → d = F(d_encrypted, n_for_d, d_for_d)
- Giải mã key chacha20 → key = F(key_encrypted, n, d)
Tuy nhiên ở trong hàm seed, sub_401B46 thay đổi giá trị của E (0x10001) d dẫn đến khi mã hóa key chacha20 sử dụng E thực chất là dùng d, và như vậy để giải mã chỉ cần sử dụng d = 0x10001 = E hay keychacha = F(keychacha_encrypted, n, 0x10001)
import struct
import binascii
def pack(s):
return struct.pack(b"<Q", s)
def packwhole(s):
r = b""
while s!= 0:
num = s & 0xFFFFFFFFFFFFFFFF
s = s >> 64
r = r+ pack(num)
return r
dcrypted = 0x5a04e95cd0e9bf0c8cdda2cbb0f50e7db8c89af791b4e88fd657237c1be4e6599bc4c80fd81bdb007e43743020a245d5f87df1c23c4d129b659f90ece2a5c22df1b60273741bf3694dd809d2c485030afdc6268431b2287c597239a8e922eb31174efcae47ea47104bc901cea0abb2cc9ef974d974f135ab1f4899946428184c
n= 0xdc425c720400e05a92eeb68d0313c84a978cbcf47474cbd9635eb353af864ea46221546a0f4d09aaa0885113e31db53b565c169c3606a241b569912a9bf95c91afbc04528431fdcee6044781fbc8629b06f99a11b99c05836e47638bbd07a232c658129aeb094ddaf4c3ad34563ee926a87123bc669f71eb6097e77c188b9bc9
d = e = 0x10001
key = (pow(dcrypted, d, n))
key = packwhole(key)
key, nonce = key.split(b'\0'*4)
ciphertext = '7F8AFA63659C5EF69EB9C3DC13E8B2313A8FE36D94863421462B6FE8AD308D2A79E8EA7B6609D8D058023D97146BF2AA608506484D970E71EA820635BA4BFC518F06E4AD692BE6255B'
ciphertext = binascii.unhexlify(ciphertext)
from Crypto.Cipher import ChaCha20
cipher = ChaCha20.new(key=key, nonce=nonce)
print(cipher.decrypt(ciphertext))
Flag: R$A_$16n1n6_15_0pp0$17e_0f_3ncryp710n@flare-on.com