HTB Sherlocks: Lockpick 2
The Lockpick 2 challenge is part of the HackTheBox Sherlocks defensive security scenarios. This challenge is already retired, and available for VIP members.
The challenge description mentions that the organization was hit with a ransomware, with it managing to encrypt a large amount of files. The organization has a stance on not paying the ransom, thus they need to have the captured sample analyzed to determine if it's possible to decrypt the files.
This write-up uses static analysis of the files, and Radare2 for the analysis of the binary.
The provided ZIP archive contains several encrypted files with the extension 24bes
, the ransomware and danger note, and another ZIP archive that contains the binary sample to be analyzed. Below is the structure of the ZIP archive for the challenge.
lockpick2.0
├── DANGER.txt
├── malware.zip
└── share
├── countdown.txt
├── expanding-horizons.pdf.24bes
└── takeover.docx.24bes
For documentation purposes, hashes of the files are collected.
452c3328667b7242132bf0821c9d4424 ./lockpick2.0/share/takeover.docx.24bes
425d610faab2eb49d5aec7f37de59484 ./lockpick2.0/share/expanding-horizons.pdf.24bes
e2edc252b5776a1e9c63c58b5328ae3a ./lockpick2.0/share/countdown.txt
62c05b0f64aa0bd8419c30216b2f106d ./lockpick2.0/DANGER.txt
bee5debabd4ab24a5d844f8bc0562e33 ./lockpick2.0/malware.zip
The DANGER.txt
file is a note to the analyst, providing some analysis instructions and the password for the ZIP archive that contains the binary sample.
Analyze the Share Files
Let us analyze the files within the share
directory.
The countdown.txt
file is the ransom note left by the ransomware, it contains a BTC address and an Onion address that can be used to reach out to the threat actors.
The BTC address can be checked through a blockchain tracker. This is outside of the scope of this analysis, so we'll just make a note of the information as indicators of compromise (IoC).
The Onion address is also being documented as an IoC, and any monitoring tools used within the organization checked for the presence of connection attempts to this address.
Reviewing the files with the extension 24bes
, they show up as being data
$ file -k expanding-horizons.pdf.24bes takeover.docx.24bes
expanding-horizons.pdf.24bes: data
takeover.docx.24bes: data
Which is expected since the data is encrypted, this can be further confirmed by checking the initial bytes of the file with a hex editor or hex dump tool such as xxd
$ xxd -l 64 expanding-horizons.pdf.24bes
00000000: 0109 8fec f14f 7659 16bf ee41 7d13 1624 .....OvY...A}..$
00000010: 2175 87e4 9f7f ca99 db8b ab7f b13f 6370 !u...........?cp
00000020: 0517 0432 99d4 ed8f 494b 24c1 5e89 21b0 ...2....IK$.^.!.
00000030: 8ec5 74d3 e96a 80c8 389b 6c05 7f6a c0ef ..t..j..8.l..j..
$ xxd -l 64 takeover.docx.24bes
00000000: 16b1 2ad0 2a87 38a4 7a5b 9e59 5b3a 8801 ..*.*.8.z[.Y[:..
00000010: 2daf 1eae bf01 5b06 5a3f b4f6 ef5c 4c00 -.....[.Z?...\L.
00000020: 1930 282b 1028 9553 ebaf ffbd 4118 9e86 .0(+.(.S....A...
00000030: 7c7b 70c3 102b f249 cd9e c0ec a7df 88f1 |{p..+.I........
There is no recognizable magic bytes for the files. At this point there's nothing else to analyze of these files.
Initial Analysis of update
The binary sample in the ZIP archive is named update
, the first step after extracting the file is to generate the hash
$ md5sum update
8b2d4bc2f26d76c1a900e53483731013 update
The hash is then checked in malware analysis services, such as VirusTotal, to determine if the sample has been submitted previously or if it may be a new malware or targeted. Checking in VirusTotal shows that there are 33 out of 65 providers have tagged this binary as malicious, it confirms the suspicion on this being the ransomware.
The tags for this entry show that the binary is packed using UPX Packer, which is a common packer used to obfuscate binaries, and threat actors also use this packer to obfuscate their malware with the intention of complicating analysis.
Let us further analyze this binary to determine how it manages to encrypts the data.
The binary file is loaded into Detect It Easy, with the intention of confirming the packing being used. There are several ways that the packing being used can be confirmed, further information on this can be found on the post Analyzing Packed Binaries.
The packed binary can be extracted by using the UPX Packer with the following command
./upx -d -o update.elf update
The unpacked binary is saved to disk with the filename update.elf
, this can now be analized by reverse engineering. Loading this binary into Radare2 for the reverse engineering.
$ r2 update.elf
[0x00001280]> aaaa
INFO: Analyze all flags starting with sym. and entry0 (aa)
INFO: Analyze imports (af@@@i)
INFO: Analyze entrypoint (af@ entry0)
INFO: Analyze symbols (af@@@s)
INFO: Analyze all functions arguments/locals (afva@@@F)
INFO: Analyze function calls (aac)
INFO: Analyze len bytes of instructions for references (aar)
INFO: Finding and parsing C++ vtables (avrr)
INFO: Analyzing methods (af @@ method.*)
INFO: Recovering local variables (afva@@@F)
INFO: Type matching analysis for all functions (aaft)
INFO: Propagate noreturn information (aanr)
INFO: Scanning for strings constructed in code (/azs)
INFO: Finding function preludes (aap)
INFO: Enable anal.types.constraint for experimental type propagation
[0x00001280]>
After loading the binary in r2
, the aaaa
command analyzes the binary to identify the different parts that make up the program. One of the first parts to check are the strings, this is achieved with the iz
command
[0x00001280]> iz
[Strings]
nth paddr vaddr len size section type string
―――――――――――――――――――――――――――――――――――――――――――――――――――――――
0 0x00003010 0x00003010 5 6 .rodata ascii \nCLIG
1 0x00003030 0x00003030 5 6 .rodata ascii \nCLIG
2 0x00003050 0x00003050 5 6 .rodata ascii \nCLIG
3 0x00003070 0x00003070 5 6 .rodata ascii \nCLIG
4 0x00003090 0x00003090 5 6 .rodata ascii \nCLIG
5 0x000030a3 0x000030a3 18 19 .rodata ascii b7894532snsmajuys6
6 0x000030b8 0x000030b8 31 32 .rodata ascii curl_easy_perform() failed: %s\n
7 0x000030d8 0x000030d8 31 32 .rodata ascii Could not open output file: %s\n
8 0x000030f8 0x000030f8 4 5 .rodata ascii .txt
9 0x000030fd 0x000030fd 4 5 .rodata ascii .pdf
10 0x00003102 0x00003102 4 5 .rodata ascii .sql
11 0x0000310b 0x0000310b 5 6 .rodata ascii .docx
12 0x00003111 0x00003111 5 6 .rodata ascii .xlsx
13 0x00003117 0x00003117 5 6 .rodata ascii .pptx
14 0x0000311d 0x0000311d 4 5 .rodata ascii .zip
15 0x00003122 0x00003122 4 5 .rodata ascii .tar
16 0x00003127 0x00003127 7 8 .rodata ascii .tar.gz
17 0x0000312f 0x0000312f 13 14 .rodata ascii countdown.txt
18 0x0000313d 0x0000313d 7 8 .rodata ascii /share/
19 0x00003145 0x00003145 15 16 .rodata ascii Update failed.\n
20 0x00003158 0x00003158 40 41 .rodata ascii Running update, testing update endpoints
21 0x00003181 0x00003181 11 12 .rodata ascii Updating %s
22 0x0000318d 0x0000318d 11 12 .rodata ascii Successful
23 0x000031a0 0x000031a0 46 47 .rodata ascii Update complete - thank you for your patience.
24 0x000031cf 0x000031cf 16 17 .rodata ascii %s/countdown.txt
25 0x000031e0 0x000031e0 32 33 .rodata ascii https://pastes.io/raw/foiawsmlsk
26 0x00003201 0x00003201 5 6 .rodata ascii %s/%s
27 0x00003210 0x00003210 30 31 .rodata ascii Could not open input file: %s\n
28 0x0000322f 0x0000322f 8 9 .rodata ascii %s.24bes
29 0x00003240 0x00003240 39 40 .rodata ascii Failed to delete the original file: %s\n
Let's analyze the strings that are shown here
- The strings show several extensions that can point to the file that the ransomware targets.
- There is also 2 strings that point to the filename of the ransom note that was included in the ZIP archive.
- There's mention of the
/share/
path which may point to theshare
directory that was included in the ZIP archive. - The extension
24bes
is also mentioned. - The URL
https://pastes.io/raw/foiawsmlsk
needs to be further investigated - There are other strings that point to status or error messages, as well as some that may either be encrypted or encoded.
Checking the URL, it shows that the ransom note is stored. This allows for the threat actor to modify the note without having to release new malware.
There's not much else to go on with the strings, so let's now look at the libraries that are used by the ransomware. The command il
shows the linked libraries.
[0x00001280]> il
[Linked libraries]
libcrypto.so.3
libcurl.so.4
libc.so.6
3 libraries
The libraries that are imported give an idea of the functionality that this ransomware has, keeping in mind that it is possible that the malware loads additional libraries dynamically during execution.
Based on these libraries, the following can be determined
- The
libcrypto
library provides the encryption capabilities, which are obvious for a ransomware. - The
libcurl
library is used to download and upload data through various protocols.
Based on the information that is known up to this point, the libcurl
appears to be used to obtain the ransom note from the URL that was found in the strings. However, it's still unknown how the encryption happens or what key is used, so further analysis of the functions is needed.
Analyzing the Functions
Let's first focus on the functions that are used from the libraries that are imported by the binary. The command ii
is used to list these functions, the output from Radare2 shows the function name
[0x00001280]> ii
[Imports]
nth vaddr bind type lib name
―――――――――――――――――――――――――――――――――――――
1 0x00001030 GLOBAL FUNC printf
2 0x00001040 GLOBAL FUNC EVP_EncryptUpdate
3 0x00001050 GLOBAL FUNC curl_global_init
4 0x00001060 GLOBAL FUNC curl_global_cleanup
5 0x00001070 GLOBAL FUNC strlen
6 0x00001080 GLOBAL FUNC OPENSSL_init_crypto
7 0x00001090 GLOBAL FUNC ERR_print_errors_fp
8 0x000010a0 GLOBAL FUNC abort
9 0x000010b0 GLOBAL FUNC EVP_EncryptInit_ex
10 0x000010c0 GLOBAL FUNC EVP_aes_256_cbc
11 0x000010d0 GLOBAL FUNC EVP_CIPHER_CTX_new
12 ---------- GLOBAL FUNC __libc_start_main
13 0x000010e0 GLOBAL FUNC sleep
14 0x000010f0 GLOBAL FUNC memcpy
15 0x00001100 GLOBAL FUNC stat
16 0x00001110 GLOBAL FUNC fclose
17 0x00001120 GLOBAL FUNC EVP_CIPHER_CTX_free
18 0x00001130 GLOBAL FUNC strrchr
19 0x00001140 GLOBAL FUNC curl_easy_setopt
20 0x00001150 GLOBAL FUNC fflush
21 0x00001160 GLOBAL FUNC fopen
22 0x00001170 GLOBAL FUNC curl_easy_cleanup
23 0x00001180 GLOBAL FUNC curl_easy_init
24 0x00001190 GLOBAL FUNC curl_easy_perform
25 0x000011a0 GLOBAL FUNC putchar
26 0x000011b0 GLOBAL FUNC strcmp
27 0x000011c0 GLOBAL FUNC fprintf
28 0x000011d0 GLOBAL FUNC curl_easy_strerror
29 0x000011e0 GLOBAL FUNC fread
30 0x000011f0 GLOBAL FUNC opendir
31 0x00001200 GLOBAL FUNC readdir
32 0x00001210 GLOBAL FUNC puts
33 0x00001220 GLOBAL FUNC EVP_EncryptFinal_ex
34 0x00001230 GLOBAL FUNC snprintf
35 0x00001240 GLOBAL FUNC closedir
36 ---------- WEAK NOTYPE _ITM_deregisterTMCloneTable
37 0x00001250 GLOBAL FUNC remove
38 ---------- WEAK NOTYPE __gmon_start__
39 ---------- WEAK NOTYPE _ITM_registerTMCloneTable
40 0x00001260 GLOBAL FUNC fwrite
42 0x00001270 WEAK FUNC __cxa_finalize
There are several functions that call my attention and that provide some relevant information
- The
EVP_aes_256_cbc
(0x10c0
) function points to the encryption algorithm that is being used by the ransomware. - There are several file handling functions, which is expected.
- The cURL functions are expected, but the most relevant one is the
curl_easy_perform
(0x1190
) since it's the one that is used to download data.
Before looking further into where the 2 functions mentioned above are used within the malware, let's look at the functions that are exported by the binary by using the iE
command.
[0x00001280]> iE
[Exports]
nth paddr vaddr bind type size lib name demangled
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
41 ---------- 0x00005140 GLOBAL OBJ 8 stdout
43 ---------- 0x00005160 GLOBAL OBJ 8 stderr
49 ---------- 0x00005140 GLOBAL OBJ 8 stdout@GLIBC_2.2.5
50 0x00003030 0x00003030 GLOBAL OBJ 19 K2
52 0x00001af3 0x00001af3 GLOBAL FUNC 394 get_key_from_url
53 ---------- 0x00005138 GLOBAL NOTYPE 0 _edata
56 0x00003000 0x00003000 GLOBAL OBJ 4 _IO_stdin_used
58 0x00003050 0x00003050 GLOBAL OBJ 19 K3
62 0x00001787 0x00001787 GLOBAL FUNC 810 main
63 0x00004130 0x00005130 GLOBAL OBJ 8 HESB
66 0x00004128 0x00005128 GLOBAL OBJ 0 __dso_handle
68 0x00001ab1 0x00001ab1 GLOBAL FUNC 66 write_binary_data
69 0x00003070 0x00003070 GLOBAL OBJ 19 K4
72 0x00001c7d 0x00001c7d GLOBAL FUNC 426 handle_directory
73 0x00002064 0x00002064 GLOBAL FUNC 0 _fini
77 0x00003090 0x00003090 GLOBAL OBJ 19 K5
79 0x0000141d 0x0000141d GLOBAL FUNC 140 xor_cipher
80 0x000014a9 0x000014a9 GLOBAL FUNC 58 write_data
81 0x0000161c 0x0000161c GLOBAL FUNC 24 handleErrors
82 0x00001280 0x00001280 GLOBAL FUNC 34 _start
84 0x000014e3 0x000014e3 GLOBAL FUNC 313 download_lyrics
87 0x00001000 0x00001000 GLOBAL FUNC 0 _init
88 ---------- 0x00005138 GLOBAL OBJ 0 __TMC_END__
94 0x0000173d 0x0000173d GLOBAL FUNC 74 encrypt
96 ---------- 0x00005160 GLOBAL OBJ 8 stderr@GLIBC_2.2.5
97 0x00004120 0x00005120 GLOBAL NOTYPE 0 __data_start
98 ---------- 0x00005170 GLOBAL NOTYPE 0 _end
105 ---------- 0x00005138 GLOBAL NOTYPE 0 __bss_start
108 0x00001e27 0x00001e27 GLOBAL FUNC 573 encrypt_file
109 0x00001634 0x00001634 GLOBAL FUNC 265 is_target_extension
115 0x00003010 0x00003010 GLOBAL OBJ 19 K1
Focusing on the lines that have the type of FUNC, there are 2 functions that appear to be relevant
- The function
get_key_from_url
(0x1af3
) points to the a key being obtained from an external source, which could potentially be the encryption key. - The
xor_cipher
(0x141d
) function points to the use of XOR encryption being used besides the AES algorithm.
Let's see if there is any relation between these 4 functions that are considered relevant in this binary. The axt
command is used to find the references to a specified address, the sintaxis of this command is axt <addr>
.
Let's look at the usage of the EVP_aes_256_cbc
function. The output shows that the function is called from the sym.encrypt_file
function, confirming that it's the encryption algorithm being used to encrypt the files.
[0x00001280]> axt 0x10c0
sym.encrypt_file 0x1f22 [CALL:--x] call sym.imp.EVP_aes_256_cbc
The curl_easy_perform
function is called by 2 different functions, with the get_key_from_url
being the more relevant one. This confirms that data is being downloaded.
[0x00001280]> axt 0x1190
sym.download_lyrics 0x159f [CALL:--x] call sym.imp.curl_easy_perform
sym.get_key_from_url 0x1bdc [CALL:--x] call sym.imp.curl_easy_perform
The get_key_from_url
function is called from the main
function.
[0x00001280]> axt 0x1af3
main 0x17c4 [CALL:--x] call sym.get_key_from_url
The xor_cipher
is called from the get_key_from_url
function, which can mean that there is one or more pieces of data that are encrypted and that are used in relation to the key that is downloaded.
[0x00001280]> axt 0x141d
main 0x186c [CALL:--x] call sym.xor_cipher
main 0x18fe [CALL:--x] call sym.xor_cipher
main 0x1990 [CALL:--x] call sym.xor_cipher
main 0x1a22 [CALL:--x] call sym.xor_cipher
sym.get_key_from_url 0x1b45 [CALL:--x] call sym.xor_cipher
The focus now shifts to look into the get_key_from_url
function, in order to determine the URL that the key is obtained from. The first step is to move the address where the function starts, this is achieved with the command s 0x1af3
, and then print the disassembled function using the pdf
command.
[0x00001af3]> pdf
; CALL XREF from main @ 0x17c4(x)
┌ 394: sym.get_key_from_url (void *arg1, int64_t arg2);
│ ; arg void *arg1 @ rdi
│ ; arg int64_t arg2 @ rsi
│ ; var uint32_t var_8h @ rbp-0x8
│ ; var int64_t var_ch @ rbp-0xc
│ ; var int64_t var_10h @ rbp-0x10
│ ; var int64_t var_14h @ rbp-0x14
│ ; var int64_t var_18h @ rbp-0x18
│ ; var uint32_t var_1ch @ rbp-0x1c
│ ; var void *s2 @ rbp-0x50
│ ; var int64_t var_90h @ rbp-0x90
│ ; var void *s1 @ rbp-0x98
│ ; var int64_t var_a0h @ rbp-0xa0
│ 0x00001af3 55 push rbp
│ 0x00001af4 4889e5 mov rbp, rsp
│ 0x00001af7 4881eca000.. sub rsp, 0xa0
│ 0x00001afe 4889bd68ff.. mov qword [s1], rdi ; arg1
│ 0x00001b05 4889b560ff.. mov qword [var_a0h], rsi ; arg2
│ 0x00001b0c bf03000000 mov edi, 3
│ 0x00001b11 e83af5ffff call sym.imp.curl_global_init
│ 0x00001b16 e865f6ffff call sym.imp.curl_easy_init
│ 0x00001b1b 488945f8 mov qword [var_8h], rax
│ 0x00001b1f 48837df800 cmp qword [var_8h], 0
│ ┌─< 0x00001b24 0f840f010000 je 0x1c39
│ │ 0x00001b2a 488b15ff35.. mov rdx, qword [obj.HESB] ; [0x5130:8]=0x30a3 str.b7894532snsmajuys6 ; char *arg3
│ │ 0x00001b31 488d8570ff.. lea rax, [var_90h]
│ │ 0x00001b38 4889c6 mov rsi, rax ; int64_t arg2
│ │ 0x00001b3b 488d05ce14.. lea rax, obj.K1 ; 0x3010 ; "\nCLIG\x0f\x1c\x1d\x01\f]\n\x18EF\x1f\x1fE\x1b"
│ │ 0x00001b42 4889c7 mov rdi, rax ; char *arg1
│ │ 0x00001b45 e8d3f8ffff call sym.xor_cipher
[..SNIP..]
The function is long, so focusing on the first lines of the function, it becomes apparent data is decrypted as the initial steps. Let's break down the information that is seen.
- At address
0x1b2a
the valueb7894532snsmajuys6
is moved into therdx
register, this would be the key being used for the encryption and decryption. - The memory address of a binary value is being stored in the
rax
register, this could be the encrypted data.
The binary data is found at the address 0x3010
, the command px
can be used to print the hex dump of the memory region, the full command is px @ 0x3010
, and the screenshot below shows the binary data that is referenced
To make it easier to handle the binary data, we'll copy the hex bytes, these being 0a434c49470f1c1d010c5d0a1845461f1f451b
. CyberChef can be used to decrypt the data, with the operations From Hex and XOR.
The encrypted data contained the URL https://rb.gy/3flsy
, which would mean that the key that is used would be downloaded from this address.
Reviewing the hex dump above, it appears that there are other URLs that are encrypted, and the CLIG
string is a common denominator among them. Decrypting these URLs can be done in the same manner as shown above, they are decoys and don't contain any valuable data, so are skipped for the purposes of this writeup.
Further reviewing the disassembled function, specifically how the data is downloaded from the URL. Checking the documentation for this function shows that this is used to perform a blocking file transfer, the example shows that the data that is downloaded is stored in a variable.
int main(void)
{
CURL *curl = curl_easy_init();
if(curl) {
CURLcode res;
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
}
The above code shows that the curl_easy_setopt
function is called to configure the curl
object, and then the curl_easy_perform
function is called to execute the download action.
The following command can be used to download the data to a file named 3flsy
.
curl -vLO --url 'https://rb.gy/3flsy'
Checking the file, it contains binary data, and it can be viewed with xxd
to obtain the data
$ xxd 3flsy
00000000: f3fc 056d a118 5eae 370d 76d4 7c4c f9db ...m..^.7.v.|L..
00000010: 9f4e fd1c 1585 cde3 a7bc c6cb 5889 f6db .N..........X...
00000020: 0144 8c79 0993 9e13 ce35 9710 b9f0 dc2e .D.y.....5......
Decrypting the Files
Now that the key has been obtained, we need to determine how to decrypt the files. Reviewing the available data, the files are encrypted using AES 256 CBC algorithm, and this can be decrypted using CyberChef.
By using the AES Decrypt operation it is possible to decrypt the files, there is relevant information displayed in the output
Invalid key length: 0 bytes
The following algorithms will be used based on the size of the key: 16 bytes = AES-128 24 bytes = AES-192 32 bytes = AES-256
Knowing that the encryption used is AES 256, the key should have a length of 32 bytes. However, the file has a size of 48 bytes, this means that it contains 16 additional bytes.
The AES encryption requires a key and an initialization vector, meaning that it's possible that this key that was downloaded contains both values. Lets extract the key and IV values from the file with the xxd
command
$ xxd -l 32 -ps 3flsy
f3fc056da1185eae370d76d47c4cf9db9f4efd1c1585cde3a7bcc6cb5889
f6db
$ xxd -s 32 -ps 3flsy
01448c7909939e13ce359710b9f0dc2e
The
-l
stops the printing of the hexdump after the specified amount of bytes. The-s
starts at the specified bytes, instead of starting at 0. The-ps
option prints only the hex values
These values are used in the AES Decrypt recipe in CyberChef, along with the encrypted files.
The encrypted data can now be restored to it's original state.
Conclusion
The threat actor has altered their tactics and now doesn't hard code all of the necessary information, while making them available through a web service that can be easily altered to change the result of the encryption.