⚠ 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