ECW 2023 - TyCoon

TyCoon is a multi-stage challenge I designed for the 2023 finals edition of the European Cyber Week. Here is the source code for this challenge.

Scenario Link to heading

Evil corp installed a malware on John’s backup server. That malware is listening for a NUKE order on UDP/9999. When received, it verifies the origin of the request using a DGA and encrypts backup files. It then deletes the symmetric key but leaves an encrypted key for later recovery. Finally, it rewrites the bootloader with a ransom note and reboots.

We are John’s friend and our task is to retrieve the secret key for backup files decryption. We have to reverse the malware (we can also restore the bootloader) in order to find the domain name used while the attack was launched. Hopefully, we find the ransomware gang panel and from there we find the key and decrypt John’s key and files in order to reveal the flag.

Writeup Link to heading

You’re given an infected.tgz and your role is to recover encrypted files within.

Let’s take a look at the infected image:

 1$ tar zxvf infected.tgz
 2infected.img
 3$ xxd -l 512 infected.img
 400000000: fa31 c08e d88e d0bc 0020 89e5 fbe8 d900  .1....... ......
 500000010: bb25 7ce8 f800 bb53 7ce8 f200 bb94 7ce8  .%|....S|.....|.
 600000020: ec00 e9b8 000a 204f 6f6f 7073 2c20 796f  ...... Ooops, yo
 700000030: 7572 2069 6d70 6f72 7461 6e74 2066 696c  ur important fil
 800000040: 6573 2061 7265 2065 6e63 7279 7074 6564  es are encrypted
 900000050: 210a 000a 2054 6f20 6465 6372 7970 7420  !... To decrypt 
1000000060: 7365 6e64 2031 2e35 2042 5443 2074 6f20  send 1.5 BTC to 
1100000070: 6d70 3979 6145 7a75 796d 344c 4548 3364  mp9yaEzuym4LEH3d
1200000080: 7278 6544 5331 7053 5172 4b69 4a7a 4a69  rxeDS1pSQrKiJzJi
1300000090: 314d 0a00 0a20 5468 656e 2065 6d61 696c  1M... Then email
14000000a0: 2075 7320 6174 2063 6f6e 7461 6374 4065   us at contact@e
15000000b0: 7669 6c63 6f72 702e 636f 6d20 7769 7468  vilcorp.com with
16000000c0: 2079 6f75 2070 6572 736f 6e61 6c20 4944   you personal ID
17000000d0: 3a20 6e47 5139 7846 3834 430a 0031 c0cd  : nGQ9xF84C..1..
18000000e0: 16cd 1968 00f0 6af0 cb50 b803 00cd 1058  ...h..j..P.....X
19000000f0: c350 b40e b00d cd10 b00a cd10 58c3 60b4  .P..........X.`.
2000000100: 0e8a 073c 0074 05cd 1043 ebf5 61c3 e8ed  ...<.t...C..a...
2100000110: ffe8 ddff c3ff ffff ffff ffff ffff ffff  ................
2200000120: ffff ffff ffff ffff ffff ffff ffff ffff  ................
2300000130: ffff ffff ffff ffff ffff ffff ffff ffff  ................
2400000140: ffff ffff ffff ffff ffff ffff ffff ffff  ................
2500000150: ffff ffff ffff ffff ffff ffff ffff ffff  ................
2600000160: ffff ffff ffff ffff ffff ffff ffff ffff  ................
2700000170: ffff ffff ffff ffff ffff ffff ffff ffff  ................
2800000180: ffff ffff ffff ffff ffff ffff ffff ffff  ................
2900000190: ffff ffff ffff ffff ffff ffff ffff ffff  ................
30000001a0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
31000001b0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
32000001c0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
33000001d0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
34000001e0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
35000001f0: ffff ffff ffff ffff ffff ffff ffff 55aa  ..............U.

When launching (qemu-system-x86_64 -hda infected.img) the image, we indeed are launching an infected machine:

The testdisk utility can both write a standard MBR code and reconstruct the partition table.

1$ cp infected.img bootable.img
2$ testdisk bootable.img

[find partitions and fix the partition table] [repair mbr bootloader code]

After running it, here is the code obtained that allows us to boot the system again:

$ xxd -l 512 bootable.img
00000000: fc31 c08e d031 e48e d88e c0be 007c bf00  .1...1.......|..
00000010: 06b9 0001 f3a5 beee 07b0 08ea 2006 0000  ............ ...
00000020: 803e b307 ff75 0488 16b3 0780 3c00 7404  .>...u......<.t.
00000030: 0806 af07 83ee 10d0 e873 f090 9090 9090  .........s......
00000040: 9090 9090 9090 9090 9090 9090 9090 9090  ................
00000050: 9090 9090 9090 9090 9090 9090 9090 9090  ................
00000060: 9090 9090 9090 9090 9090 9090 9090 9090  ................
00000070: 9090 9090 9090 9090 9090 9090 9090 bebe  ................
00000080: 07b0 00b9 0400 803c 0075 6efe c083 c610  .......<.un.....
00000090: e2f4 31db b40e be9d 078a 0eaf 07ac d0e9  ..1.............
000000a0: 7302 cd10 08c9 75f5 b03a cd10 31c0 cd16  s.....u..:..1...
000000b0: 3c00 74f8 be8b 07b9 0200 e8ba 003c 0d74  <.t..........<.t
000000c0: b43c 6172 063c 7a77 022c 2088 c3be 9d07  .<ar.<zw., .....
000000d0: 8a0e af07 acd0 e973 0438 c374 0608 c975  .......s.8.t...u
000000e0: f3eb afb8 0d0e 31db cd10 8d84 6200 3c07  ......1.....b.<.
000000f0: 7507 b01f a2af 07eb 9931 d2b9 0100 3c04  u........1....<.
00000100: 7411 73f3 30e4 b104 d2e0 bebe 0701 c68a  t.s.0...........
00000110: 16b3 07bf 0500 56f6 c280 7431 b441 bbaa  ......V...t1.A..
00000120: 5552 cd13 5a5e 5672 1e81 fb55 aa75 18f6  UR..Z^Vr...U.u..
00000130: c101 7413 8b44 088b 5c0a be8d 0789 4408  ..t..D..\.....D.
00000140: 895c 0ab4 42eb 0c8a 7401 8b4c 02b8 0102  .\..B...t..L....
00000150: bb00 7c50 c606 8f07 01cd 1358 5e73 054f  ..|P.......X^s.O
00000160: 75b4 eb93 813e fe7d 55aa 75f6 ea00 7c00  u....>.}U.u...|.
00000170: 00be 8307 b90a 0050 b40e 31db accd 10e2  .......P..1.....
00000180: fb58 c354 6573 7444 6973 6b0d 0a10 0001  .X.TestDisk.....
00000190: 0000 7c00 0000 0000 0000 0000 0031 3233  ..|..........123
000001a0: 3446 0000 414e 4454 6d62 7200 0202 021f  4F..ANDTmbr.....
000001b0: c700 0080 0000 0000 ffff ffff ffff 8020  ............... 
000001c0: 2100 83df 130c 0008 0000 0020 0300 00df  !.......... ....
000001d0: 140c 8225 174e 0028 0300 0000 1000 0025  ...%.N.(.......%
000001e0: 184e 8315 5005 0028 1300 00d8 2c00 0000  .N..P..(....,...
000001f0: 0000 0000 0000 0000 0000 0000 0000 55aa  ..............U.

Using testdisk, we found an recovered the /backup folder but also discovered some suspicious files under /root:

1$ ls -R recovery/
2recovery/:
3backup  root
4
5recovery/backup:
6Artists.csv  secrets.db.enc
7
8recovery/root:
9implant  key

The /backup/Artists.csv file looks clean, we can give it to John. However, its secrets.db.enc looks like it has been encrypted, let’s fix that for him.

It looks like /root/implant is the malware we’re looking for! There is also a /root/key file.

 1/root$ file *
 2implant: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
 3key:     data
 4/root$ xxd key
 500000000: 0ac8 6aa7 ce0f 9af6 fbff 91f9 c6d9 9dee  ..j.............
 600000010: 9450 a659 a22e 0ddc b51b 1a27 2361 6b53  .P.Y.......'#akS
 700000020: 4ca7 b6e4 3852 4edb bca3 79fa 53c9 0fcd  L...8RN...y.S...
 800000030: d29a d806 630f 9a0c 04ab 584f 1d07 b7bc  ....c.....XO....
 900000040: 64d7 a492 edac 647b f5f1 4a50 e162 46c6  d.....d{..JP.bF.
1000000050: 3724 718a 957c 3c97 3b46 458d 2da7 d72e  7$q..|<.;FE.-...
1100000060: a60e 6af8 4b29 b9f8 ed40 a4ec e333 99d7  ..j.K)...@...3..
1200000070: 598e 8ee5 6517 02c6 7151 032a cb18 acca  Y...e...qQ.*....
1300000080: 8449 8751 0017 0e82 6555 7717 aeeb 1501  .I.Q....eUw.....
1400000090: f09e 0ffd de19 d0d6 f681 8862 a7be 26da  ...........b..&.
15000000a0: daac 0c8a 92ef 64b6 a129 8566 1b17 23a1  ......d..).f..#.
16000000b0: 8f42 c5c1 d08b 76e2 688f 2ee1 60f0 0072  .B....v.h...`..r
17000000c0: 0168 2453 5b90 a8e7 5479 3e9f 4114 2d9a  .h$S[...Ty>.A.-.
18000000d0: 0dfc f8f9 dd6b ed5f fe45 3040 92d2 aff4  .....k._.E0@....
19000000e0: 858d d4f6 0ee5 c785 6f45 db84 77e5 3742  ........oE..w.7B
20000000f0: 33ec 8c60 d9fd ae72 0989 83d4 53f5 f012  3..`...r....S...

There is no symbols but we can extract a lot of juicy strings from the implant:

 1shred -zuf key.bin
 2shred /etc/hosts
 3/root/secret
 4/user/secret
 5/root/backup
 6/user/backup
 7/dev/sda
 8/dev/hda
 9/dev/sdb
10/dev/hdb
11/dev/sdc
12/dev/hdc
13/dev/sdd
14/dev/hdd
15/dev/sde
16/dev/hde
17/dev/sdf
18/dev/hdf
19/dev/sdg
20/dev/hdg
21current domain is %s.%s
22openssl enc -aes-256-cbc -salt -pbkdf2 -in 
23 -out 
24.enc -pass file:./key.bin
25shred -uzf 
26 Ooops, your important files are encrypted!
27 To decrypt send 1.5 BTC to mp9yaEzuym4LEH3drxeDS1pSQrKiJzJi1M
28 Then email us at contact@evilcorp.com with you personal ID: nGQ9xF84C

We can understand some bits of information out of those strings:

  1. The files have been encrypted using a key.bin key file that was then deleted using shred, so we don’t try to recover it.
  2. Both the secret and backup directories of root and user folders have been targeted.
  3. A format-string is mentioning what looks like a domain name.
  4. The /etc/hosts file also has been shredded.

Trying to use the left-over /root/key file leads to nothing. What about the domain thing?

Before handling incoming commands, the implant is checking the domain name associated with the sender. We can recover the domain using the domain name generation algorithm at 0x4011F1:

 1void __fastcall __noreturn sub_4011F1(unsigned int a1)
 2{
 3  __int64 v1; // rax
 4  __int64 v2; // rax
 5  __int64 v3; // rax
 6  unsigned int v4; // [rsp+8h] [rbp-2E0h] BYREF
 7  int cmd; // [rsp+Ch] [rbp-2DCh] BYREF
 8  char v6[32]; // [rsp+10h] [rbp-2D8h] BYREF
 9  __int16 v7[65]; // [rsp+30h] [rbp-2B8h] BYREF
10  char v8[255]; // [rsp+B2h] [rbp-236h] BYREF
11  char v9[311]; // [rsp+1B1h] [rbp-137h] BYREF
12
13  while ( 1 )
14  {
15    while ( 1 )
16    {
17      do
18        v4 = 0x80;
19      while ( recvfrom_4049E0(a1, &cmd, 4LL, 0LL, v7, &v4) != 4 );
20      if ( !(unsigned int)getnameinfo_4025E0(v7, v4, (__int64)v8, 0xFFu, (__int64)v6, 0x20u, 2) )
21      {
22        generate_domain_401517();
23        memset(v9, 0, 0xFFuLL);
24        v1 = strcat_40A300((__int64)v9, (__int64)&g_domain_name);
25        v2 = strcat_40A300(v1, (__int64)&string_dot_414000);
26        v3 = strcat_40A300(v2, (__int64)&string_bzh_414002);
27        if ( !(unsigned int)sub_40A440(v8, v3) )
28          break;
29      }
30    }
31    if ( !(unsigned int)sub_40A510(&cmd, "ABRT", 4LL) )
32      abrt();
33    if ( !(unsigned int)sub_40A510(&cmd, "NUKE", 4LL) )
34      nuke();
35    cmd = 4932417;
36    sub_405AA0(a1, &cmd, 4LL, 0LL, v7, v4);
37  }
38}

The domain name is generated at generate_domain_401517 and is appended with a dot at string_dot_414000 and a “bzh” TLD at string_bzh_414002.

Most of the job now is to reverse the generate_domain_401517 algorithm:

 1unsigned __int64 generate_domain_401517()
 2{
 3  int *v0; // rax
 4  __m128 day; // xmm1
 5  __m128 year; // xmm0
 6  char *domain; // rcx
 7  __m128 month; // xmm2
 8  unsigned __int64 v5; // xmm3_8
 9  unsigned __int64 result; // rax
10  __int64 v7; // [rsp+8h] [rbp-10h] BYREF
11
12  v7 = time_40B0C0(0LL);
13  v0 = (int *)localtime_40B020(&v7);
14  day = (__m128)(unsigned __int64)v0[3];
15  year = (__m128)(unsigned __int64)(v0[5] + 1900);
16  domain = (char *)&g_domain_name;
17  month = (__m128)(unsigned __int64)(v0[4] + 1);
18  do
19  {
20    ++domain;
21    month = _mm_xor_ps(
22              (__m128)_mm_slli_epi64((__m128i)_mm_and_ps(month, (__m128)xmmword_4141A0), 4u),
23              (__m128)_mm_srli_epi64((__m128i)_mm_xor_ps((__m128)_mm_slli_epi64((__m128i)month, 2u), month), 1u));
24    year = _mm_xor_ps(
25             _mm_and_ps((__m128)_mm_slli_epi64((__m128i)year, 8u), (__m128)xmmword_4141B0),
26             (__m128)_mm_srli_epi64((__m128i)_mm_xor_ps((__m128)_mm_slli_epi64((__m128i)year, 3u), year), 4u));
27    day = _mm_xor_ps(
28            _mm_and_ps((__m128)_mm_slli_epi64((__m128i)day, 4u), (__m128)xmmword_4141C0),
29            (__m128)_mm_srli_epi64((__m128i)_mm_xor_ps((__m128)_mm_slli_epi64((__m128i)day, 0x10u), day), 2u));
30    v5 = month.m128_u64[0] ^ year.m128_u64[0] ^ day.m128_u64[0];
31    result = v5 / 0x19;
32    *(domain - 1) = v5 % 0x19 + 97;
33  }
34  while ( (char *)&g_domain_name + 9 != domain );
35  end_of_g_domain_name_41A631 = 0;
36  return result;
37}

The nine domain name characters are generated from a struct tm structure obtained from the localtime(time(NULL)) call. The assembly for the loop may be more helpful than the decompiler to recover the DGA:

 1loc_401577:
 2movaps  xmm7, xmm2      ; Move Aligned Four Packed Single-FP
 3movaps  xmm3, cs:xmmword_4141A0 ; Move Aligned Four Packed Single-FP
 4xor     edx, edx        ; Logical Exclusive OR
 5inc     rcx             ; Increment by 1
 6psllq   xmm7, 2         ; Packed Shift Left Logical (Qword)
 7movaps  xmm6, xmm7      ; Move Aligned Four Packed Single-FP
 8movaps  xmm7, xmm0      ; Move Aligned Four Packed Single-FP
 9xorps   xmm6, xmm2      ; Bitwise Logical XOR for Single-FP Data
10psllq   xmm7, 3         ; Packed Shift Left Logical (Qword)
11andps   xmm2, xmm3      ; Bitwise Logical And for Single-FP
12psrlq   xmm6, 1         ; Packed Shift Right Logical (Qword)
13psllq   xmm2, 4         ; Packed Shift Left Logical (Qword)
14movaps  xmm3, xmm7      ; Move Aligned Four Packed Single-FP
15xorps   xmm2, xmm6      ; Bitwise Logical XOR for Single-FP Data
16xorps   xmm3, xmm0      ; Bitwise Logical XOR for Single-FP Data
17movaps  xmm6, xmm1      ; Move Aligned Four Packed Single-FP
18psllq   xmm0, 8         ; Packed Shift Left Logical (Qword)
19psrlq   xmm3, 4         ; Packed Shift Right Logical (Qword)
20movaps  xmm7, xmm2      ; Move Aligned Four Packed Single-FP
21andps   xmm0, xmm5      ; Bitwise Logical And for Single-FP
22psllq   xmm6, 10h       ; Packed Shift Left Logical (Qword)
23xorps   xmm0, xmm3      ; Bitwise Logical XOR for Single-FP Data
24movaps  xmm3, xmm6      ; Move Aligned Four Packed Single-FP
25xorps   xmm3, xmm1      ; Bitwise Logical XOR for Single-FP Data
26psllq   xmm1, 4         ; Packed Shift Left Logical (Qword)
27xorps   xmm7, xmm0      ; Bitwise Logical XOR for Single-FP Data
28psrlq   xmm3, 2         ; Packed Shift Right Logical (Qword)
29andps   xmm1, xmm4      ; Bitwise Logical And for Single-FP
30xorps   xmm1, xmm3      ; Bitwise Logical XOR for Single-FP Data
31movaps  xmm3, xmm7      ; Move Aligned Four Packed Single-FP
32xorps   xmm3, xmm1      ; Bitwise Logical XOR for Single-FP Data
33movq    rax, xmm3       ; Move 64 bits
34div     rsi             ; Unsigned Divide
35add     edx, 61h ; 'a'  ; Add
36mov     [rcx-1], dl
37cmp     rdi, rcx        ; Compare Two Operands
38jnz     loc_401577      ; Jump if Not Zero (ZF=0)

You can either debug, emulate it or reimplement it.

Here is a C transcript or the original DGA:

 1#include <stdlib.h>
 2#include <stdint.h>
 3#include <stdio.h>
 4#include <err.h>
 5
 6int main(int ac, char**av)
 7{
 8    if (ac != 4)
 9        errx(EXIT_FAILURE, "usage: %s <month> <day> <year>\n", av[0]);
10
11    char subdomain[10];
12    uint64_t month = (uint64_t)atoi(av[1]);
13    uint64_t day = (uint64_t)atoi(av[2]);
14    uint64_t year = (uint64_t)atoi(av[3]);
15    for(unsigned i = 0 ; i < sizeof(subdomain) - 1; ++i)
16    {
17        month = ((month ^ 4 * month) >> 1) ^ 16 * (month & 0xFFFFFFF2);
18        year = ((year ^ 8 * year) >> 4) ^ ((year & 0xFFFFFFF4) << 8);
19        day = ((day ^ (day << 16)) >> 2) ^ ((day & 0xFFFFFFFA) << 4);
20        subdomain[i] = (char)(((year ^ month ^ day) % 25) + 'a');
21    }
22
23    subdomain[sizeof(subdomain)-1] = 0;
24    printf("subdomain: %s\n", subdomain);
25}

With such algorithm, we are able to recover the domain name generated for the date of the attack:

1$ ./dga 07 26 2023
2subdomain: uxigxmxvm

Now let’s hack back those hackers at uxigxmxvm.bzh!

It’s a static website leaking data from those hackers victims. Can we find John’s data over there? Definitely not, but as we explore the website, it stands out that each company data is leaked under /files/<ID> and that ID looks pretty much like the one we were given in the corrupted bootloader. By Looking at /files/nGQ9xF84C/, we recover a key.pem key file.

 1$ wget uxigxmxvm.bzh/files/nGQ9xF84C/key.pem
 2$ cat key.pem
 3-----BEGIN PRIVATE KEY-----
 4MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDChc3ubr7clJtt
 5RGVTIKmaZdRDMETKZIOLIehLH3U52QJ1Xa1ruZ+PC1BKWVNPqS/E3x9p5CSec6Ba
 6nJtn7KI1m7Qxu8uCtkr8xCZ1jFge1KYmYh4u21r7fJzdXM9qyxgML3knKi4GV0BA
 7clZ6UXX6m/432CzufbmDhMRSd7XfdQed8X2SK7gwtePOmqY4AmnRl9ozLtR+qTff
 8sv5FtZQcxyA5FHR9MQlTaYzg+mKrvtax+vKZg+INfolQobXKoi17nLwHhZ5Ow8Ha
 9xyLkqMfBC3hej6qo2yBY5JzxgIGhufjXV1F82IbFFx6B7JY2hmvhjiAG4dqfkHWw
10q+ojOYoVAgMBAAECggEAHvLAjEuaBH3aP/yumEFX6o+Clrv9EyxXEhGaI9WZ8NME
11xUfbBGAbMYbNxpPGoaPGd1k+Zg+yVHQLUk9HRGC4KrTz6tO43GTaB7QXda1CBIm1
12nja5xvUHA9WpdBNE5stFhK7K5OnHj80IP8NRMeh4T5/LnEvsU3dfnBFJVjY4kpgE
13tXsZxBU8NaR6XtZEx1ohtUwVMZth4ZMgNs1b24acQaH0KK40sG8sbzqhdA2Qk+cQ
14XKPyji6uS4stzpxQrgC4eyD2aM/Oq0g7zSf+HfhCY03Fvx97G4rDRtpP4KIW2Htg
15gkoaVwOBXiNsqMD935n09Xwplk9Lawioe/f8LQEQQwKBgQDsr6SLMDn4oMQFombN
16lgKcejI93XDOHpJS9Oxh/Y6yLdAeQUaLUX7DfR90XiTLRuP6BiljWN8Dh66nrklo
17JBYUW1FIZeqL2Woa14BT0MZN14x0c3wunqR1zQWlGmwROc+phAJp/Y2Q/AyDDkrn
18wvV01/tT0BJ+0jcfcI5gV134wwKBgQDSZV7Pf3GOD2kH5jjCBHWNXVf45UJktqjk
193I//rOfy4XO2bR8TKufo8mM8lOmRxsThpWgxnKjE+6qisSahqwObB9R73aebPFBq
20KMCNOMsjj5+isuhhwaH5rtS1OUCBpM+mkjkZmua6tJtiH1UhEPUJoPmEf4+CcGF8
213RTW1jeERwKBgQCtHPKek1FzVjLJZDUI3VVfmcixkwt01stzPYy/RzNdg0CbQGcW
22cy7iUNv2wvzqaRlJv8P51ACZll3aaxFpyCsWDIxxBYn9a7G9nC1SIHtKaANlESqc
239o+XUbN1RNQR7VTDybfySe+HQbLtEEEdLm1VXruGW8OLWrnSlwKr2Hr0/QKBgQDK
24EK2UH1QSGd7HxWYxgFLd6A47bwPq8jsXQnXSGl/SNpEJXZgAsq50XYbNgj8o0Hv6
25Mv/01f6I4SOqiPUPQ818sXJzXBhC0RRyQJ1dhHQkvSWV/rmMWYmU4UJMoqW/XWhJ
26FBpe6xQ5sIejH3CFB2IvUzkQ9eoAXqpiX3pKMwaytQKBgQCYVJ5KtW20f9Ox7qJZ
272MDMu7L5gj/6C02fGN+bLLrudyxBcYvqiSzwE7Q5ZG6TRUGWSNASl9SqsK24MI2E
28LdgAWInYleCS8Jali1wZtXGxtpvdRYlLiDtV58Qr7RBcpfLDVoAxyiCFtWearZqZ
29AJbu+b9e33Z4SzI17gTHeRvcRw==
30-----END PRIVATE KEY-----

On John’s machine, the implant used a key.bin file for file encryption before deleting it. However, it left a key file.

1$ openssl rsautl -decrypt -inkey key.pem -in recovery/root/key -out key.bin
2$ cat key.bin
3UyhXwZQ+cxB+QunKoBA2Nbzj5SfhoBVFtFUyoS25EMg=

It looks like we recovered the 256 bits key used for file encryption. By looking at the implant strings, aes cbc was used, let’s try that on secrets.db.enc:

1$ openssl enc -d -aes-256-cbc -pbkdf2 -in recovery/backup/secrets.db.enc -out secrets.db -pass file:key.bin
2$ file secrets.db 
3secrets.db: SQLite 3.x database, last written using SQLite version 3038001
4$ sqlite3 secrets.db .dump
5PRAGMA foreign_keys=OFF;
6BEGIN TRANSACTION;
7CREATE TABLE secrets(flag text not null);
8INSERT INTO secrets VALUES('ECW{all_your_file_belong_to_us}');
9COMMIT;

Awesome, here is your flag!