الْحَمْدُ لِلّهِ الَّذِي هَدَانَا لِهَـذَا وَمَا كُنَّا لِنَهْتَدِيَ لَوْلا أَنْ هَدَانَا اللّهُ
Date: 25/09/2026
By: Y0un15
Little Talk
I participated in CAT CTF 2026 with team BR0K3N SH311 and we qualified for the finals, and I am happy to share that we took first place

These are some walkthroughs for the DFIR and rev challenges
I didn’t manage to solve them all during the CTF, but I had to look into them again after the CTF
so let’s start
67 iq

this was the easy rev challenge and it was the only solved rev challenge during the CTF
with a quick file check and running the binary, it was an ELF binary Sudoku game

so I opened it in IDA to see the logic and how to crack it
After decompiling main and checking the logic

I found that we can just avoid the game logic and the checks and let the binary print the flag and do the work for us
This is possible because the code checks the input, and if it was incorrect it prints incorrect and exits and the decryption of the flag will not happenes
So, I just had to jump over all these checks and let the binary decode and print the flag
To do so, we need to stop after the initialization of the v18 variable and then jump to the decoding for loop

So I made a breakpoint at v14 = v18; and before the exit after printing Incorrect so the initialization of the key finishes
I started the remote Linux debugger
after adding a random 81-digit input
124367589586249317793158264312594678857621493469783152945836721278415936631972845
letting the do-while loop run so the key gets initialized

then I grabbed the offset to jump to
or I could just hit Ctrl+N to set the RIP value to that address
Then i ran the game and it decoded the flag and printed it without having to staticlly doing it or brute force anything
CATF{QU1CK_BR41N_W4RM_UP}
Protege
I made some progress on this challenge during the CTF but couldn’t finish it in time
In this challenge we were given 3 files: a DLL, an EXE file, and a PCAP file
So let’s do a quick analysis of the given files
Protege.exe
After loading it in IDA and giving it a look, I found that Protege.exe is mostly a launcher or wrapper for the game, and the logic is inside the DLL it loads
Function sub_140001BF0 probably handles the key input for the game, then it calls sub_140001810() after the window is created

And that function is responsible for loading the DLL, it uses GetProcAddress to resolve a number of exported functions:
Game_ApiVersion
Game_Create
Game_Initialize
Game_Destroy
Game_Update
Game_Render
Game_OnKey
Game_ShouldQuit
when the program exits, it calls Game_Destroy, unloads the DLL with FreeLibrary, and starts the cleanup process
I tried to run this binary and debug it, but I was hit with this error

We will see why this happened and how to avoid it after we analyze the DLL
Protege.dll
From the EXE we understood that this DLL gets loaded and some functions get resolved, so we need to understand what they do
But first we need to see what the function Game_Initialize does
Because it was the last call in the EXE before unloading the DLL and starting the cleanup process(and also it has the DirectX11 message thing), so let’s analyze it first

It has two calls: one to sub_180001680 and the other to sub_180002C30

Analyzing function sub_180001680

1- It first calls sub_180002980, which calls the APIs GetTickCount64, GetCurrentProcessId, and BCryptGenRandom. From the dynamic analysis I did during the CTF, this function creates some kind of UUID identifier, so I will call it create_UUID

2- Then calls to sub_180001CB0 and sub_180001A20. During the CTF I found that they have an AES signature and do some important initialization, so let’s break each one down in detail
First the function sub_180001CB0

It first obtains the current system time with GetSystemTimeAsFileTime and then creates the string <UUID>|<timestamp>|PAYLOADKEY
GetSystemTimeAsFileTime(&SystemTimeAsFileTime);
pcbBinary = (*&SystemTimeAsFileTime - 116444736000000000LL) / 0x989680uLL;
if ( sub_180002C70(Buffer, 0xA0uLL, "%s|%llu|PAYLOADKEY") < 0 )
Then a call to function sub_1800013C0, which takes some interesting arguments
- 160-character buffer
- a 32-byte key:
EE1CB148F919FE9AC9E13DD62730A15D082A915773D2E7C04AEDA19CE1045233
And inside this function is the AES function call sub_180001000 that I previously found with the FindCrypt plugin during the CTF
v10 = sub_180001000(1LL, a1, v9, a2, a3, &byte_180004388, 16, &pbBinary, &cbBinary);
byte_180004388(FF969B2B4C3B76EB64362BAE3E044145), which is the IV for the AES encryption
__int64 __fastcall sub_180001000(
int a1,
UCHAR *a2,
ULONG a3,
UCHAR *a4,
ULONG cbSecret,
_OWORD *a6,
int a7,
UCHAR **a8,
ULONG *a9)
{
UCHAR *v12; // rbx
HANDLE ProcessHeap; // rax
UCHAR *v14; // rax
int v15; // edi
NTSTATUS v16; // eax
HANDLE v17; // rax
UCHAR *v18; // rsi
NTSTATUS v19; // eax
HANDLE v20; // rax
ULONG *v21; // rax
ULONG v22; // ecx
HANDLE v24; // rax
HANDLE v25; // rax
ULONG cbOutput; // [rsp+50h] [rbp-69h] BYREF
UCHAR pbOutput[4]; // [rsp+54h] [rbp-65h] BYREF
BCRYPT_KEY_HANDLE phKey; // [rsp+58h] [rbp-61h] BYREF
BCRYPT_ALG_HANDLE phAlgorithm; // [rsp+60h] [rbp-59h] BYREF
ULONG pcbResult; // [rsp+68h] [rbp-51h] BYREF
UCHAR v31[4]; // [rsp+6Ch] [rbp-4Dh] BYREF
int v32; // [rsp+70h] [rbp-49h]
ULONG *v33; // [rsp+78h] [rbp-41h]
UCHAR pbIV[16]; // [rsp+80h] [rbp-39h] BYREF
UCHAR pbInput[16]; // [rsp+90h] [rbp-29h] BYREF
__int128 v36; // [rsp+A0h] [rbp-19h]
v32 = a1;
v33 = a9;
v12 = 0LL;
phAlgorithm = 0LL;
phKey = 0LL;
*pbOutput = 0;
pcbResult = 0;
*v31 = 0;
cbOutput = 0;
*pbInput = xmmword_180004480;
v36 = xmmword_180004490;
if ( !a2 || !a3 || !a4 || !a6 || a7 != 16 || !a8 || !a9 || ((cbSecret - 16) & 0xFFFFFFE7) != 0 || cbSecret == 40 )
return 0LL;
*a8 = 0LL;
*a9 = 0;
if ( BCryptOpenAlgorithmProvider(&phAlgorithm, L"AES", 0LL, 0) < 0 )
goto LABEL_35;
if ( BCryptSetProperty(phAlgorithm, L"ChainingMode", pbInput, 0x20u, 0) < 0 )
goto LABEL_35;
if ( BCryptGetProperty(phAlgorithm, L"ObjectLength", pbOutput, 4u, &pcbResult, 0) < 0 )
goto LABEL_35;
if ( !*pbOutput )
goto LABEL_35;
if ( BCryptGetProperty(phAlgorithm, L"BlockLength", v31, 4u, &pcbResult, 0) < 0 )
goto LABEL_35;
if ( *v31 != 16 )
goto LABEL_35;
ProcessHeap = GetProcessHeap();
v14 = HeapAlloc(ProcessHeap, 0, *pbOutput);
v12 = v14;
if ( !v14 || BCryptGenerateSymmetricKey(phAlgorithm, &phKey, v14, *pbOutput, a4, cbSecret, 0) < 0 )
goto LABEL_35;
v15 = v32;
*pbIV = *a6;
v16 = v32
? BCryptEncrypt(phKey, a2, a3, 0LL, pbIV, 0x10u, 0LL, 0, &cbOutput, 1u)
: BCryptDecrypt(phKey, a2, a3, 0LL, pbIV, 0x10u, 0LL, 0, &cbOutput, 1u);
if ( v16 < 0 || !cbOutput || (v17 = GetProcessHeap(), (v18 = HeapAlloc(v17, 0, cbOutput)) == 0LL) )
{
LABEL_35:
if ( phKey )
BCryptDestroyKey(phKey);
if ( phAlgorithm )
BCryptCloseAlgorithmProvider(phAlgorithm, 0);
if ( v12 )
{
if ( *pbOutput )
memset(v12, 0, *pbOutput);
v25 = GetProcessHeap();
HeapFree(v25, 0, v12);
}
return 0LL;
}
*pbIV = *a6;
if ( v15 )
v19 = BCryptEncrypt(phKey, a2, a3, 0LL, pbIV, 0x10u, v18, cbOutput, &cbOutput, 1u);
else
v19 = BCryptDecrypt(phKey, a2, a3, 0LL, pbIV, 0x10u, v18, cbOutput, &cbOutput, 1u);
if ( v19 < 0 || !cbOutput )
{
if ( cbOutput )
memset(v18, 0, cbOutput);
v24 = GetProcessHeap();
HeapFree(v24, 0, v18);
goto LABEL_35;
}
BCryptDestroyKey(phKey);
BCryptCloseAlgorithmProvider(phAlgorithm, 0);
if ( *pbOutput )
memset(v12, 0, *pbOutput);
v20 = GetProcessHeap();
HeapFree(v20, 0, v12);
v21 = v33;
v22 = cbOutput;
*a8 = v18;
*v21 = v22;
return 1LL;
}
this is AES encryption in CBC mode using the key and IV mentioned above
And after that, the encrypted data is base64 encoded and wrapped into this JSON:
{"data":"<base64 ciphertext>"}
So function sub_1800013C0 is responsible for the AES encryption, base64 encoding, and wrapping that data
Then function sub_180002600 sends the request to api.protege.test:8080 with a POST request to /api/protege/init, as we will see in the PCAP file
Then function sub_180001870 extracts this base64 string from the JSON field, saves it to pszString, and then uses CryptStringToBinaryA to decode that base64

Then another call to sub_180001000, which likely decrypts the AES-encrypted response
So basically function sub_180001CB0 does this:
- Generates a UUID and formats it as
UUID | timestamp | PAYLOADKEY - Then function
sub_1800013C0encrypts the buffer using AES-CBC with the embedded key and IV - Base64 encodes and wraps that data
- Sends a POST request to
api.protege.test:8080/api/protege/init - Receives the server response, Base64 decodes it, and decrypts it using AES-CBC
Then function sub_180001A20 locates a resource inside that DLL and AES decrypts it using the server response session key and IV. To get those, we need to inspect and decode the provided PCAP file, but for now let’s skip ahead and analyze the rest of it
After decrypting the resource with the server key and IV, it checks if the header is a valid PE or not, and if it is, it then dumps it inside the temp folder under the filename ProtegePayload_<tickcount>_<pid>.exe
And if the writing fails, it wipes the decrypted EXE from memory and closes any open handles
{
FileW = CreateFileW(Buffer, 0x40000000u, 0, 0LL, 2u, 0x2100u, 0LL);
if ( FileW == -1LL )
{
*Buffer = 0;
}
else
{
v20 = v17;
v21 = v16;
if ( v16 )
{
while ( 1 )
{
nNumberOfBytesToWrite = 0;
if ( !WriteFile(FileW, v20, v21, &nNumberOfBytesToWrite, 0LL) || !nNumberOfBytesToWrite )
break;
v20 += nNumberOfBytesToWrite;
v21 -= nNumberOfBytesToWrite;
if ( !v21 )
goto LABEL_22;
}
CloseHandle(FileW);
DeleteFileW(Buffer);
*Buffer = 0;
}
else
{
LABEL_22:
FlushFileBuffers(FileW);
CloseHandle(FileW);
v4 = 1;
}
}
}
}
}
if ( v17 )
{
if ( v16 )
memset(v17, 0, v16);
ProcessHeap = GetProcessHeap();
HeapFree(ProcessHeap, 0, v17);
}
return v4;
}
So let’s inspect the PCAP and try to find and decode the server IV and key response
Protege.pcap
The PCAP has five streams as shown:

Knowing the key,IV, and the AES mode, we can decrypt stream 0 and get the server response key and IV
I used CyberChef to do this, and here is the recipe
Now we have the key and IV to decrypt the embedded PE from the resources
Decrypting the PE from resources
we can use this recipe
key:9VLcUNZ2DakyH8DlHJnhaXSTqKnBqkjU4q5GXM05NJQ=
iv:aQihqtqOPybZEhsPPMTsIA==
I dumped the resource from Protege.exe file with PE-bear
Before using it we need to skip some bytes, so the easier way is to use Resource Hacker and save the RCDATA 101 to disk

After loading the .bin file into the recipe, we get the PE file we need, which is Stage 2

So i saved it and named it Stage2.exe
Analyzing Stage 2
Loading the PE inside IDA Pro, I immediately noticed the same pattern, where:
- The key for the next stream in the PCAP
- The IV
- Likely the game logic or what caused the next streams
I extracted the key and the IV and went to decrypt the stream 1 data (I noticed that I could decrypt streams 1, 2, and 3 since they have the same UUID and different responses from the server but different arguments READY 1, READY 2, and READY 3)

Now we have this pattern:
{"nonce":"HtYlBM39yePbyDy5","key":"MOQXaM8/HD0fdcrXGwTvv0aqF8tziN5cqrtsiBFZDAU=","opcode":17,"encryptedvalue":"/eL5wRsawt/rWccc"}
so I needed to understand what READY 1, 2, or 3 does and what the game logic does:
Stage 2 connects to the server and sends:
3e1ac478-09d3-45a7-bdd4-a08c77190761|1790217336|READY|1
and it receives and decrypts the server response:
{"nonce":"HtYlBM39yePbyDy5","key":"MOQXaM8/HD0fdcrXGwTvv0aqF8tziN5cqrtsiBFZDAU=","opcode":17,"encryptedvalue":"/eL5wRsawt/rWccc"}
then it calls the function sub_1400021C0, which seems to have the main game logic
This function has a lot of math, and the function calls inside it also involve heavy math. It would be very painful to go through each one of them and understand what they do, so I had to guess in general what the flow was
The function sub_140001000 gets called before the string {\"data\":\"", so it’s likely an encryption or decryption algorithm (or both, like the logic in the previous stage) maybe AES?
then function sub_140001FA0 (honestly, I couldn’t figure out what it does at first) so I asked an LLM and it was resolving these APIs:
a1 + 160 → WinHttpOpen
a1 + 168 → WinHttpConnect
a1 + 176 → WinHttpOpenRequest
a1 + 184 → WinHttpSendRequest
a1 + 192 → WinHttpReceiveResponse
a1 + 200 → WinHttpQueryDataAvailable
a1 + 208 → WinHttpReadData
a1 + 216 → WinHttpCloseHandle
so we can name it HTTP_POST_and_json
then function sub_140001CD0 and the call inside it to sub_140001550 (I assumed it was the other encryption algorithm used before in the server response we decrypted)
As we saw from the server response, there was a nonce, key, opcode, and encrypted value
So it’s most likely ChaCha20 or Salsa20 (I searched which algorithms use the intrinsics _mm_xor_ps and _mm_loadu_si128 along with a nonce and key)
It would be very painful for me to try solving the state machine and decrypting the flag statically, so I tried to go for the dynamic solution
Solving Dynamically
Why is this possible?
Because when sub_140001FA0 tries to send the POST request it fails, but maybe I can add the server response from the PCAP directly in memory and force it to continue instead of terminating
After rebasing IDA to match x64dbg so it would be easier for me to see and jump wherever I want:

I set some breakpoints and did some renaming, then started the binary
I also changed the argument to match the one sent in the PCAP:
--uuid=3e1ac478-09d3-45a7-bdd4-a08c77190761 from file we will chose command line and we can change it there
then i hit F9 until i hit the breakpoint at game_logic
then i stepped into it and continued until i hit the HTTP_POST function while running FakeNet
when i hit the breakpoint, i grabbed the R8 pointer and followed it in the dump, and this is what i got:

now we just need to edit the memory and put the correct server data:
{"data":"p/uQIcjC8ogDrT69LVNaTLkRqz90/A/YFWCHpRiaUEMkcZ5y8+p0JE8br99BJtdSXxCN3sP5Nk8EVKRDVn+8oiKBYdvspbTgi5ZRNO5MAoGE9Y0Fi+3HDbHJhqt3ktsit1qOmlaLYPe2xrgY8yaSgLLaYEfdJvZdAHbvP4Auhl4himw5WxNvRirBQNABbRK9"}

after that I continued until it hit the breakpoint again, and I added the next state:
{"data":"nqyfVZesbqDevb9ks1ZypO6RUUEin9vLN+rFNYzxBWY+5UH+Lq/L5T4Qizq5nkpBR8W0o9CX5EH3eSw2lmeZAg/dU5BPm+5Qdpl8qroWfytDCr2dJPaXhDndZOHWJt3/LwAxRs0j2stFD3XBUTy6FlIcCEixj+EPQomiv80jyQHBb3SCVJ1RlQlDAf5FpA96kLxnzGivBvap83I8gp1xFw=="}
make sure to add 00 (the null terminator) at the end of the string you put
I kept continuing until I hit the HTTP breakpoint again, but there was no POST request after it, so I just kept going until it hit again and I saw the FakeNet response in memory

then I patched it and kept continuing
until it hit again
now for the third one:
{"data":"DaLkiXZejLYEZHcvWj6v8fwI3XDTWxwnxQgT0sbcQXFJKpJY+yla/nTaN+uevoaXlBOtk90zyOxaq6JFnnvWoU50BT0wySi7LNVCp8Zj08DZMzj5391IzFien79zsMNU8ML2ZlH/TgvTtprEJtRsnPr2C2wWe0ncVu4fvydHm0XuC4ROgIN8U1NvQFWQvj5t6VmdhOZxUsMpAELra3o2yg=="}
now we have reached the last one, READY 4:
After I patched it:
{"data":"hTSgVq8jpWTqa7OhDiCVGuKw8O2EwupXURHsvJUMAB8HC3t12DMHFNeQDjB7vaP5otXJwpjX1G51fWcG3NCC/PUP/hj8z1VJ17kKWS2QUrpA6W799JWk44K3947esKLBaCwSV5IuEBf/Ka+cWfgL3C1Sr9AdYddiGOreAfBrtR3r0P1BRkdTfYisaQIvZwpdWMJD/0DZYl6K/ndC7HJocghRvcJbpRHp46PjgTz6pM7pQdMCu2E3bSbd1yuzjaRIhW11GXUdwGvRauyl0LxbEaPp+7vpLK1r8/mtAWG06Ek="}
I needed to see what the decrypted server output was to get the flag
so I placed a breakpoint on the function sub_140001CD0,and another one right before the check for CATF (the flag format):

And just like that, we solved the challenge without any need for heavy math or custom Python code
CACATF{Wh3n_4_m4St3r_1s_T4k3n_0uT_bY_h1s_Pr0t3g3_a42c534ccaf4fb1377abcd44}
Another solution
Creating a mock server to respond to the binary requests
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
RESPONSES = [
# READY|1
'{"data":"p/uQIcjC8ogDrT69LVNaTLkRqz90/A/YFWCHpRiaUEMkcZ5y8+p0JE8br99BJtdSXxCN3sP5Nk8EVKRDVn+8oiKBYdvspbTgi5ZRNO5MAoGE9Y0Fi+3HDbHJhqt3ktsit1qOmlaLYPe2xrgY8yaSgLLaYEfdJvZdAHbvP4Auhl4himw5WxNvRirBQNABbRK9"}',
# READY|2
'{"data":"nqyfVZesbqDevb9ks1ZypO6RUUEin9vLN+rFNYzxBWY+5UH+Lq/L5T4Qizq5nkpBR8W0o9CX5EH3eSw2lmeZAg/dU5BPm+5Qdpl8qroWfytDCr2dJPaXhDndZOHWJt3/LwAxRs0j2stFD3XBUTy6FlIcCEixj+EPQomiv80jyQHBb3SCVJ1RlQlDAf5FpA96kLxnzGivBvap83I8gp1xFw=="}',
# READY|3
'{"data":"DaLkiXZejLYEZHcvWj6v8fwI3XDTWxwnxQgT0sbcQXFJKpJY+yla/nTaN+uevoaXlBOtk90zyOxaq6JFnnvWoU50BT0wySi7LNVCp8Zj08DZMzj5391IzFien79zsMNU8ML2ZlH/TgvTtprEJtRsnPr2C2wWe0ncVu4fvydHm0XuC4ROgIN8U1NvQFWQvj5t6VmdhOZxUsMpAELra3o2yg=="}',
# READY|4
'{"data":"hTSgVq8jpWTqa7OhDiCVGuKw8O2EwupXURHsvJUMAB8HC3t12DMHFNeQDjB7vaP5otXJwpjX1G51fWcG3NCC/PUP/hj8z1VJ17kKWS2QUrpA6W799JWk44K3947esKLBaCwSV5IuEBf/Ka+cWfgL3C1Sr9AdYddiGOreAfBrtR3r0P1BRkdTfYisaQIvZwpdWMJD/0DZYl6K/ndC7HJocghRvcJbpRHp46PjgTz6pM7pQdMCu2E3bSbd1yuzjaRIhW11GXUdwGvRauyl0LxbEaPp+7vpLK1r8/mtAWG06Ek="}',
]
request_count = 0
class ReplayHandler(BaseHTTPRequestHandler):
def do_POST(self):
global request_count
length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(length)
print(f"[*] Request #{request_count}: {body[:80]}...")
resp = RESPONSES[min(request_count, len(RESPONSES)-1)]
request_count += 1
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(resp.encode('utf-8'))
print("Fake C2 Server listening on 0.0.0.0:8080...")
HTTPServer(('0.0.0.0', 8080), ReplayHandler).serve_forever()
Make sure to add api.protege.test to your hosts file:
Add-Content -Path "C:\Windows\System32\drivers\etc\hosts" -Value "`n127.0.0.1 api.protege.test"
Then you need to run the Stage 2 file with the argument --uuid=3e1ac478-09d3-45a7-bdd4-a08c77190761:
stage2.exe --uuid=3e1ac478-09d3-45a7-bdd4-a08c77190761

it was a nice challenge and shoutout to k1r17oo for this awesome challenge

CrackMe
We are given a 64-bit Windows PE executable named chall.exe. Running the executable prompts us to enter 5 separate serial parts:
Enter serial part 1/5:
The challenge description provided a crucial hint: “Who cares about SERIAL??!”
This hints that there is a shortcut somewhere we can take that will make things much easier
![]()
After loading the PE file into IDA Pro, we locate the main verification routine at sub_1400018FF
The program first initializes a 40-byte buffer to hold the flag and then enters a loop 5 times. Each time, it asks the user to enter a serial part , and if there is no input error, it calls the validation function:
if ( !sub_1400017EC(i, Buffer, Size_4, &Size) )
{
puts("Invalid serial.");
return 1;
}
After all 5 parts are passed, it hashes the final 40-byte string and compares it against 0x774C42D40DDD0703:
if ( sub_140001605(v6, v9) == 0x774C42D40DDD0703LL )
printf("Validation complete. Flag: %.*s\n", v9, v6);

I found 5 blobs in the .rdata section (I was searching for any encrypted blobs so I could trace back from there) and they have a reference in the function sub_1400017EC.
Blob_0 : 0A 5E 4D CE 0D B9 D9 10 A3 D1 D7 99
Blob_1 : 69 70 CA 6A B3 8B 86 F9 C0 FD DE 22
Blob_2 : D6 EF 9E C5 CB 73 BE 83 0F 54 28 EB
Blob_3 : EA 56 08 E6 3C A1 C6 EE 43 F5 64 D7
Blob_4 : DC 16 F5 EB EA FC 80 66 4E 4B 48
So let’s take a deep dive into the function sub_1400017EC to understand what those five blobs were
-
sub_1400014AA hashes the input to produce a 4-byte key
-
sub_14000150F decrypts the encrypted blob with the calculated key

The program checks that the first decrypted byte is 0x10 or not and calls sub_140001588 to do some other calculation
If this check fails, it proceeds to clean up and exit the function, and continues the loop if it wasn’t the last one.
Then the function sub_14000166B is called, and it has some interesting checks that make me call it the parser.
Because it checks whether the output from XORing with the key derived from the input starts with 0x10, 0x21, 0x30, or 0xFF.
For example, let’s assume the output is 0x10 0x03 0x45 0x26 0x48 0x21 0x11 0x30 0xFF.
0x10: Readslength, which in our case is0x3, then copieslengthbytes from the stream into a buffer.0x21: Readsadd_value, then addsadd_valueto every copied output byte (out[j] += add_value).0x30:No-operation; skips to the next tag0xFF:Marks end of bytecode and returns success
so becuase we have some fixed prefix 0x10 we can brute force all the blobs and then parse them as fucntino sub_14000166B would do and we ill have the flag and this is the shortcut the was hinting for it in the description
And this is the brute-force script with the parsing logic:
#!/usr/bin/env python3
BLOBS = [
bytes.fromhex("0A 5E 4D CE 0D B9 D9 10 A3 D1 D7 99"),
bytes.fromhex("69 70 CA 6A B3 8B 86 F9 C0 FD DE 22"),
bytes.fromhex("D6 EF 9E C5 CB 73 BE 83 0F 54 28 EB"),
bytes.fromhex("EA 56 08 E6 3C A1 C6 EE 43 F5 64 D7"),
bytes.fromhex("DC 16 F5 EB EA FC 80 66 4E 4B 48"),
]
CRCS = [0x6020, 0xDDF5, 0x4837, 0x5932, 0xCC1A]
def crc16_ccitt(data: bytes) -> int:
crc = 0xFFFF
for byte in data:
crc ^= (byte << 8)
for _ in range(8):
if crc & 0x8000:
crc = ((crc << 1) ^ 0x1021) & 0xFFFF
else:
crc = (crc << 1) & 0xFFFF
return crc
def decrypt(blob: bytes, key: bytes) -> bytes:
return bytes(b ^ ((key[i % 4] + 13 * i) & 0xFF) for i, b in enumerate(blob))
def parse_tlv(data: bytes):
pos = 0
out = bytearray()
while pos < len(data):
tag = data[pos]
pos += 1
if tag == 0xFF: # END / SUCCESS
return bytes(out)
elif tag == 0x10: # COPY
if pos >= len(data): return None
length = data[pos]
pos += 1
if pos + length > len(data): return None
out = bytearray(data[pos:pos + length])
pos += length
elif tag == 0x21: # ADD
if pos >= len(data): return None
add_val = data[pos]
pos += 1
for j in range(len(out)):
out[j] = (out[j] + add_val) & 0xFF
elif tag == 0x30: # NOP
continue
else:
return None
return bytes(out)
def main():
flag_parts = []
for i, (blob, expected_crc) in enumerate(zip(BLOBS, CRCS)):
k0 = blob[0] ^ 0x10
copy_len = 5 if i == 4 else 6
k1 = ((blob[1] ^ copy_len) - 13) & 0xFF
found = False
for k2 in range(256):
if found:
break
for k3 in range(256):
key = bytes([k0, k1, k2, k3])
decrypted = decrypt(blob, key)
if crc16_ccitt(decrypted) == expected_crc:
output = parse_tlv(decrypted)
if output:
text = output.decode("ascii")
print(f"Blob {i} -> {text}")
flag_parts.append(text)
found = True
break
print()
print("FINAL FLAG:", "".join(flag_parts))
if __name__ == "__main__":
main()
Running it will give us the flag:
Blob 0 -> CATF{3
Blob 1 -> oBAD_s
Blob 2 -> 3n6s_h
Blob 3 -> 1s_r3g
Blob 4 -> 4rd5}
FINAL FLAG: CATF{3oBAD_s3n6s_h1s_r3g4rd5}
I was planning to get the keys from the brute force, patch them in memory, and let the binary run with a couple of forced jumps to see the flag, parsing the decrypted pieces directly in Python was much faster and i lost my interist in middel of it lol
CATF{3oBAD_s3n6s_h1s_r3g4rd5}
Vault
I was given a PE binary to analyze and find the flag inside the Vault. During the CTF a hint was dropped: You need to know what does software breakpoint actually cost you
It was a huge hint cuz I didn’t know why the exception error was happening So let’s take a look into the code flow and try to find the flag
Trying To Understand Code Flow
Loading the binary in IDA Pro, we end up at the start function:
So let’s start analyzing the function sub_7FF676F611B0 and the interesting calls inside it
Then I launched the binary to see what happens and potentially trace back from that:

Now we have these strings: Welcome to my vault, Enter the password:, nope, so I located them in IDA and cross-referenced them
I ended up with this function sub_7FF676F61975 which I will call prob_main:

This is easily crackable, but during the CTF I realized this is a fake path and it gives us this:
y0u_g0t_My_p4ss_but_th1s_1s_n0t_th3_fl4g_y0u_n33d_t0_l00k_3ls3wh3r3
and here is the recipe to do so cyberchef_recipe
So back to the binary and let’s see what else we have
Searching the strings in IDA, I found the string Correct Flag, and tracing back from it leads to function TlsCallback_1

I have potentially found the correct path check
There were some TLS callback functions and they looked promising, so I started to analyze the code flow around them
TlsCallback_0
void __fastcall TlsCallback_0(__int64 a1, int a2)
{
const WCHAR *CommandLineW; // rax
DWORD flOldProtect; // [rsp+48h] [rbp-18h] BYREF
int pNumArgs; // [rsp+4Ch] [rbp-14h] BYREF
HLOCAL hMem; // [rsp+50h] [rbp-10h]
int i; // [rsp+5Ch] [rbp-4h]
if ( a2 == 1 )
{
pNumArgs = 0;
CommandLineW = GetCommandLineW();
hMem = CommandLineToArgvW(CommandLineW, &pNumArgs);
if ( pNumArgs > 1 )
{
VirtualAlloc(0x20260000, 0x1000uLL, 0x3000u, 4u);
WideCharToMultiByte(0, 0, *(hMem + 1), -1, 0x202600A0, 111, 0LL, 0LL);
}
else
{
for ( i = 0; i < strlen(Str); ++i )
Str[i] ^= 0xAAu;
OutputDebugStringA("\n\n\n\n\n");
OutputDebugStringA(Str);
OutputDebugStringA("\n\n");
VirtualProtect(&off_7FF676F71040, 0x20uLL, 4u, &flOldProtect);
off_7FF676F71040 = 0LL;
off_7FF676F71048 = 0LL;
off_7FF676F71050 = 0LL;
off_7FF676F71058 = 0LL;
}
if ( hMem )
LocalFree(hMem);
}
}
A TLS callback runs before the entry point or main function
Analyzing the TlsCallback_0 function, I found that the program checks if it was run with an argument or not, so there are two paths:
-
If supplied with an argument: It allocates memory at
0x20260000and saves the argument to the address0x202600A0(base + 0xA0) with a max size of 111 bytes (you can check the manual here) -
If no argument was provided: It does an XOR decryption on the variable
Str:
which gives this message:
k1r17oo thinks the challenge is too hard for you so I gave you this message as hint.
I used this recipe
It prints that message to the debugger’s log, then clears pointers to disable callbacks 1 through 6:

TlsCallback_1
void __fastcall TlsCallback_1(__int64 a1, int a2)
{
HANDLE CurrentProcess; // rax
DWORD v3; // [rsp+38h] [rbp-68h] BYREF
BYTE v4[4]; // [rsp+3Ch] [rbp-64h] BYREF
BYTE v5[2]; // [rsp+40h] [rbp-60h] BYREF
__int16 v6; // [rsp+42h] [rbp-5Eh]
int v7; // [rsp+44h] [rbp-5Ch]
int v8; // [rsp+48h] [rbp-58h]
__int64 v9; // [rsp+4Ch] [rbp-54h]
__int64 v10; // [rsp+54h] [rbp-4Ch]
DWORD pdwDataLen; // [rsp+5Ch] [rbp-44h] BYREF
BYTE v12[8]; // [rsp+60h] [rbp-40h] BYREF
__int64 v13; // [rsp+68h] [rbp-38h]
HCRYPTKEY hKey; // [rsp+78h] [rbp-28h] BYREF
HCRYPTHASH phHash; // [rsp+80h] [rbp-20h] BYREF
HCRYPTPROV phProv; // [rsp+88h] [rbp-18h] BYREF
DWORD flOldProtect; // [rsp+94h] [rbp-Ch] BYREF
BYTE *pbData; // [rsp+98h] [rbp-8h]
if ( a2 == 1 )
{
MEMORY[0x20260058] = 0;
MEMORY[0x20260000] = puts;
MEMORY[0x20260008] = ExitProcess;
MEMORY[0x20260060] = IsDebuggerPresent;
MEMORY[0x20260068] = CryptAcquireContextA;
MEMORY[0x20260070] = CryptImportKey;
MEMORY[0x20260078] = CryptSetKeyParam;
MEMORY[0x20260080] = CryptEncrypt;
MEMORY[0x20260088] = CryptDestroyKey;
MEMORY[0x20260090] = CryptReleaseContext;
MEMORY[0x20260110] = 0x2CBC6E028388438ALL;
MEMORY[0x20260118] = 0x5DB4CCED9A2C83F0LL;
MEMORY[0x20260120] = 0x1861983A8A862DADLL;
MEMORY[0x20260128] = 0x9331EA8FFA98EBB5uLL;
MEMORY[0x20260130] = 0x589403358138DDB0LL;
MEMORY[0x20260138] = 0x3E50A9A6549D13B6LL;
strcpy(0x20260140, "Correct Flag");
VirtualProtect(&BaseAddress, 0x1DA0uLL, 0x40u, &flOldProtect);
pbData = GetModuleHandleA(0LL);
phProv = 0LL;
phHash = 0LL;
hKey = 0LL;
if ( CryptAcquireContextA(&phProv, 0LL, 0LL, 0x18u, 0xF0000000) )
{
if ( CryptCreateHash(phProv, 0x8003u, 0LL, 0, &phHash) )
{
CryptHashData(phHash, pbData, 0x80u, 0);
pdwDataLen = 16;
CryptGetHashParam(phHash, 2u, v12, &pdwDataLen, 0);
v5[0] = 8;
v5[1] = 2;
v6 = 0;
v7 = 26126;
v8 = 16;
v9 = *v12;
v10 = v13;
if ( CryptImportKey(phProv, v5, 0x1Cu, 0LL, 0, &hKey) )
{
*v4 = 2;
CryptSetKeyParam(hKey, 4u, v4, 0);
v3 = 7584;
CryptDecrypt(hKey, 0LL, 0, 0, &BaseAddress, &v3);
CryptDestroyKey(hKey);
}
CryptDestroyHash(phHash);
}
CryptReleaseContext(phProv, 0);
}
CurrentProcess = GetCurrentProcess();
FlushInstructionCache(CurrentProcess, &BaseAddress, 0x1DA0uLL);
}
}
It resolves Win32 API pointers into 0x20260000, then copies a 48-byte blob of data to that address:
8A438883026EBC2C F0832C9AEDCCB45D AD2D868A3A986118
B5EB98FA8FEA3193 B0DD38813503945 8B6139D54A6A9503E
I tried to use x64dbg to run the binary so I could see what those functions do:



What I understood is:
- It hashes the first 0x80 bytes of the loaded EXE with MD5
I checked Microsoft documentation to understand the rest: CryptCreateHash | CryptAcquireContextA | CryptSetKeyParam | CryptExportKey
- in CryptAcquireContextA:
0x18urepresentsPROV_RSA_AES - in CryptCreateHash:
0x8003umaps directly toCALG_MD5 - in CryptGetHashParam: retrieves the MD5 hash buffer used as the AES key material
- v7 = 26126; ->
aiKeyAlg = CALG_AES_128(0x660E)
So in the code flow after calculating the MD5 hash of the header, it uses that hash as the key for AES-ECB(we will know the mode later) to decrypt a 0x1DA0-byte region at BaseAddress
This decrypts the code and data in memory including the huge blob in the .data section
Now we have a clear view of what the challenge is about
Correct Flag Flow
The idea of the challenge is : setting any software breakpoint writes 0xCC (INT 3) into the binary and it modifies the first 0x80 bytes of the PE header, the MD5 hash changes, which corrupts the AES decryption key and crashes the program with an exception
So we can’t use any software breakpoints
Instead we used hardware breakpoints on the IsDebuggerPresent function and the strcpy call and run the binary with a dummy argument so we enter the correct path and hopefully see the flag
Finding the Correct Flag
To do so, I stopped x64dbg from setting software breakpoints on all TLS callbacks, placed a hardware breakpoints on IsDebuggerPresent and strcpy, and let the binary run
First I placed my hardware breakpoints while TLS callback event pausing was enabled:
Then I removed the auto software breakpoints and kept only the hardware ones
I ran the debugger, but couldn’t hit IsDebuggerPresent. So I restarted and broke at TLS callbacks reaching TlsCallback_1 (it showes in x64dbg as TlsCallback_3), placed a hardware breakpoint on it, removed all auto breakpoints as before, and tried again

This huge calculation was the decryption algorithm, so I placed a hardware breakpoint after it and pressed F9:
And watched memory at that point to see what was there:

which we will find out later this is the AES key
After a lot of tries, I couldn’t reach the strcpy I was waiting for and kept hitting an exception or a freeze
So I had to think of another way
I decided to decompile this huge function in IDA to make it easier to understand where the flag check happens
I set a hardware breakpoint in TlsCallback_1 (set a normal breakpoint, opened the Breakpoints table, right-clicked -> Edit -> changed type to Hardware Execute)
Then I added a command line argument (via Debugger -> Process options -> added dummy_input as an argument)
After that, I ran it and hit the breakpoint inside TlsCallback_1 then stepped over CryptDecrypt
Then checked the huge data blob inside .data, which was now decrypted
I sefined it with elected all of it, undU, converted it to code with C, and created a function with P
Here is what I got:
unsigned __int64 sub_7FF606FA9000()
{
__int64 v0; // rsi
unsigned __int64 v1; // r12
__int64 v2; // rdx
__int64 v3; // rbx
unsigned __int64 v4; // r8
unsigned __int64 v5; // rdi
__int64 v6; // rax
unsigned __int64 v7; // r11
unsigned __int64 v8; // rbp
unsigned __int64 v9; // r10
unsigned __int64 v10; // rcx
unsigned __int64 v11; // r9
unsigned __int64 v12; // rsi
unsigned __int64 v13; // rdx
__int64 v14; // rbx
unsigned __int64 v15; // rdi
unsigned __int64 v16; // r11
unsigned __int64 v17; // rax
unsigned __int64 v18; // r8
unsigned __int64 v19; // rbp
unsigned __int64 v20; // rcx
unsigned __int64 v21; // rsi
unsigned __int64 v22; // r10
unsigned __int64 v23; // rbx
unsigned __int64 v24; // rdx
unsigned __int64 v25; // r9
unsigned __int64 v26; // rdi
unsigned __int64 v27; // r11
unsigned __int64 v28; // rax
unsigned __int64 v29; // r8
unsigned __int64 v30; // rbp
unsigned __int64 v31; // r10
unsigned __int64 v32; // rcx
unsigned __int64 v33; // rsi
unsigned __int64 v34; // rbx
unsigned __int64 v35; // rdx
unsigned __int64 v36; // r9
unsigned __int64 v37; // rdi
unsigned __int64 v38; // rax
unsigned __int64 v39; // r11
unsigned __int64 v40; // rbp
unsigned __int64 v41; // r10
unsigned __int64 v42; // rcx
unsigned __int64 v43; // r8
unsigned __int64 v44; // rsi
unsigned __int64 v45; // rdx
unsigned __int64 v46; // rbx
unsigned __int64 v47; // rdi
unsigned __int64 v48; // r11
unsigned __int64 v49; // rax
unsigned __int64 v50; // r9
unsigned __int64 v51; // rbp
unsigned __int64 v52; // rcx
unsigned __int64 v53; // rsi
unsigned __int64 v54; // r10
unsigned __int64 v55; // rbx
unsigned __int64 v56; // rdx
unsigned __int64 v57; // r8
unsigned __int64 v58; // rdi
unsigned __int64 v59; // r11
unsigned __int64 v60; // rax
unsigned __int64 v61; // r9
unsigned __int64 v62; // rsi
unsigned __int64 v63; // r10
unsigned __int64 v64; // rcx
unsigned __int64 v65; // rbp
unsigned __int64 v66; // rbx
unsigned __int64 v67; // rdx
unsigned __int64 v68; // r8
unsigned __int64 v69; // rdi
unsigned __int64 v70; // rax
unsigned __int64 v71; // r11
unsigned __int64 v72; // rsi
unsigned __int64 v73; // rcx
unsigned __int64 v74; // r10
unsigned __int64 v75; // r9
unsigned __int64 v76; // r12
unsigned __int64 v77; // rdx
unsigned __int64 v78; // rbx
unsigned __int64 v79; // rdi
unsigned __int64 v80; // rax
unsigned __int64 v81; // rsi
unsigned __int64 v82; // r8
unsigned __int64 v83; // rbp
unsigned __int64 v84; // rcx
unsigned __int64 v85; // r11
unsigned __int64 v86; // r10
unsigned __int64 v87; // rbx
unsigned __int64 v88; // rdx
unsigned __int64 v89; // r9
unsigned __int64 v90; // rdi
unsigned __int64 v91; // rsi
unsigned __int64 v92; // rax
unsigned __int64 v93; // r8
unsigned __int64 v94; // r10
unsigned __int64 v95; // r11
unsigned __int64 v96; // rcx
unsigned __int64 v97; // r12
unsigned __int64 v98; // rbx
unsigned __int64 v99; // rdx
unsigned __int64 v100; // r9
unsigned __int64 v101; // rdi
unsigned __int64 v102; // rax
unsigned __int64 v103; // r10
unsigned __int64 v104; // rbp
unsigned __int64 v105; // rcx
unsigned __int64 v106; // r11
unsigned __int64 v107; // r8
unsigned __int64 v108; // rsi
unsigned __int64 v109; // rdx
unsigned __int64 v110; // r9
unsigned __int64 v111; // rdi
unsigned __int64 v112; // rbx
unsigned __int64 v113; // rax
unsigned __int64 v114; // r10
unsigned __int64 v115; // rbp
unsigned __int64 v116; // rcx
unsigned __int64 v117; // rsi
unsigned __int64 v118; // r8
unsigned __int64 v119; // r11
unsigned __int64 v120; // rdx
unsigned __int64 v121; // r9
unsigned __int64 v122; // rdi
unsigned __int64 v123; // rbx
unsigned __int64 v124; // rax
unsigned __int64 v125; // rcx
unsigned __int64 v126; // rsi
unsigned __int64 v127; // r10
unsigned __int64 v128; // r8
unsigned __int64 v129; // rbp
unsigned __int64 v130; // r11
unsigned __int64 v131; // rdx
unsigned __int64 v132; // r9
unsigned __int64 v133; // rdi
unsigned __int64 v134; // rcx
unsigned __int64 v135; // rbx
unsigned __int64 v136; // rsi
unsigned __int64 v137; // rax
unsigned __int64 v138; // r10
unsigned __int64 v139; // r8
unsigned __int64 v140; // rbp
unsigned __int64 v141; // rdx
unsigned __int64 v142; // r11
unsigned __int64 v143; // rdi
unsigned __int64 v144; // r9
unsigned __int64 v145; // rsi
unsigned __int64 v146; // rcx
unsigned __int64 v147; // rax
unsigned __int64 v148; // r10
unsigned __int64 v149; // r8
unsigned __int64 v150; // rbx
unsigned __int64 v151; // rdx
unsigned __int64 v152; // rdi
unsigned __int64 v153; // r11
unsigned __int64 result; // rax
v0 = (__ROL8__(0x56C82252074AF8A6LL, 5) + 0x14A9FEA74BE3A6DELL) ^ 0x6A09E667F3BCC908LL;
v1 = (0xAB20066D38940427uLL * __ROL8__(0xA54FF53A5F1D36F1uLL, 27) + 0x34CAFDC015EFE9DCLL) ^ 0x3C6EF372FE94F82BLL;
v2 = 0x3436138118ACD01LL * (__ROL8__(0x87E706D9FA5D42A2uLL, 26) ^ 0x1F83D9ABFBLL) - 0x5AB00AC5A0E2C90FLL;
v3 = (__ROR8__(0x8256EA66EBADFFE8uLL, 15) + 3082918450LL) ^ 0x510E527FADE682D1LL;
v4 = ((8 * v0) ^ 0xC291ED7C134393FEuLL) - 0x64FA9773D4C193E1LL;
v5 = (0x6C0354B08AF423EBLL * __ROL8__(0x5BE0CD19137E2179LL, 16) + 0x6E8CD353D0E6FA15LL) ^ 0x1F83D9ABFB41BD6BLL;
v6 = 0x164913AEFA2D522DLL * ((v1 >> 17) ^ __ROR8__(v0 + 0x1A2934C4B72DCA57LL, 27)) + 0x5BE0CD19137E2179LL;
v7 = v0 + ((v2 << 6) ^ 0xDC3A03054A4F78BCuLL);
v8 = ((v3 ^ 0x86CB43AC55BE2FA3uLL) - 0xB5ED72B2F399D4BLL * __ROL8__(v1, 23)) ^ 0x7834B93328CD37A8LL;
v9 = v2 ^ ((v5 >> 27) + __ROR8__(v3 - 0x58F7EEE312785623LL, 19));
v10 = v1 + 0x1BF268749AF259BFLL * ((v4 >> 20) ^ __ROL8__(v2 + 0x2BF0F855C97ED9D9LL, 30));
v11 = v3 + ((v6 << 34) ^ ((v4 << 8) ^ 0xE03E62012DEF7100uLL | HIBYTE(v4) ^ 0xB0u));
v12 = v4 ^ ((v7 ^ 0x53F3CBD86F8AA07FLL) - 0x569B53F9A8700F39LL * __ROL8__(v5, 12));
v13 = v5 - 0x2DE8A7FDDD1B1269LL * ((v8 >> 13) ^ __ROR8__(v6 - 0x4A3039E59D18F7EBLL, 23));
v14 = v6 ^ ((v10 >> 20) + __ROR8__(v7 + 0x88099669A8065C3LL, 30));
v15 = v7 ^ ((v9 ^ 0xB83435F533D3351LL) + 0x3FC7DDB508F6079LL * __ROL8__(v8, 19));
v16 = v10 ^ ((v12 >> 23) + __ROR8__(v9 - 0x500A58023D8F2E2BLL, 23));
v17 = v8 - 0x35D858092971E525LL * ((v11 >> 16) ^ __ROR8__(v10 - 0x3F0EAFFD475486DDLL, 30));
v18 = v9 + ((v13 << 30) ^ ((v11 << 12) ^ 0x25EFD6195101D000LL | (v11 >> 52) ^ 0x446));
v19 = v11 ^ ((v14 ^ 0x6E2C6BAFCA6C895BLL) - 0x13F49C231CCEB417LL * __ROL8__(v12, 8));
v20 = v12 - 0x5A3476F9EF500FE3LL * ((v15 >> 9) ^ __ROR8__(v13 + 0x6D87DF1CAEE96C1DLL, 19));
v21 = v13 ^ ((v17 >> 16) + __ROL8__(v14 + 0x4329132E1BC4B07FLL, 30));
v22 = v14 + ((v16 << 23) ^ ((v15 << 23) ^ 0x1E31CCF1F5800000LL | (v15 >> 41) ^ 0x25327D));
v23 = v17 ^ ((v19 >> 19) + __ROR8__(v16 - 0x22F18E70E688575LL, 27));
v24 = v15 - 0x771A01EC0C1AE61DLL * ((v18 >> 12) ^ __ROR8__(v17 + 0x46C8B3C2CBEEB70FLL, 26));
v25 = v16 + ((v20 << 26) ^ ((v18 << 16) ^ 0xA7FB67D6249D0000uLL | HIWORD(v18) ^ 0x432A));
v26 = v18 ^ ((v21 ^ 0x8725CE6FC36614E7uLL) + 0x27785FCBD2E9B5D9LL * __ROR8__(v19, 5));
v27 = v20 ^ ((v24 >> 12) + __ROL8__(v21 - 0x7E592235600F273FLL, 26));
v28 = v19 - 0x3448479D63918127LL * ((v22 >> 5) ^ __ROR8__(v20 + 0x159546A5CDD57047LL, 15));
v29 = v21 + ((v23 << 19) ^ ((v22 << 27) ^ 0xB17909A0E8000000uLL | (v22 >> 37) ^ 0x2ACA54B));
v30 = v22 ^ ((v25 ^ 0xA0A16E730435817BuLL) + 0x26EE3EA0E3873527LL * __ROR8__(v24, 16));
v31 = v24 ^ ((v26 >> 15) + __ROR8__(v23 + 0x71DF60777ABEFF71LL, 31));
v32 = v23 + ((v28 << 22) ^ ((v25 << 20) ^ 0x611C737FBB00000LL | (v25 >> 44) ^ 0xCE994));
v33 = v25 ^ ((v27 ^ 0x95C587F23D1E2DB3uLL) - 0x3BC96EA93DB09911LL * __ROR8__(v26, 9));
v34 = v28 ^ ((v30 >> 8) + __ROL8__(v27 - 0x41724F36132E8EEBLL, 22));
v35 = v26 + 0x596116B8E83D59DBLL * ((v29 >> 36) ^ __ROR8__(v28 + 0x1BC73DD1F76EF2F9LL, 11));
v36 = v27 + ((v31 << 15) ^ ((v29 << 31) ^ 0x3134F6BE80000000LL | (v29 >> 33) ^ 0x4A5775A));
v37 = v29 ^ ((v32 ^ 0xDC48544951C000A3uLL) + 0x4103011B4D842A1FLL * __ROR8__(v30, 20));
v38 = v30 + 0x54D35166622E4FFFLL * ((v33 >> 29) ^ __ROL8__(v31 - 0x41D63F3CD71E8CB3LL, 9));
v39 = v31 + ((v35 << 18) ^ ((v32 << 24) ^ 0x1DBCCD81F3000000LL | (v32 >> 40) ^ 0x4786ED));
v40 = v32 ^ ((v34 ^ 0xBB07E60D26D2282DuLL) + 0x2CC669B5107C5885LL * __ROR8__(v33, 13));
v41 = v35 ^ ((v37 >> 4) + __ROL8__(v34 + 0x5151AE2E8275E45FLL, 18));
v42 = v33 - 0x54BDAFAE8B8112CDLL * (HIDWORD(v36) ^ __ROR8__(v35 - 0x5F0AF8FCB72A9B0BLL, 7));
v43 = v34 + ((v38 << 11) ^ ((v36 << 35) ^ 0x48B39D2800000000LL | (v36 >> 29) ^ 0x30F77FFD8LL));
v44 = v36 ^ ((v39 ^ 0x7C77B4F5824FF00BLL) + 0x5CF22A9E109CEEF5LL * __ROR8__(v37, 24));
v45 = v37 - 0x475DAFCDEBC34B1BLL * ((v40 >> 25) ^ __ROL8__(v38 - 0x63D8762F29865B73LL, 13));
v46 = v38 ^ (HIDWORD(v42) + __ROL8__(v39 + 0x13586CB70C64A37FLL, 7));
v47 = v39 ^ ((v41 ^ 0xE78E36E67D4898BDuLL) - 0x64F29CDFED5E6EB5LL * __ROR8__(v40, 17));
v48 = v42 ^ ((v44 >> 35) + __ROL8__(v41 - 0x21CE924BD7F54039LL, 14));
v49 = v40 - 0x2E0F74634726CD53LL * ((v43 >> 28) ^ __ROL8__(v42 - 0x4FA8C162A86A2A43LL, 6));
v50 = v41 + ((v45 << 7) ^ ((v43 << 39) ^ 0x259AB8000000000LL | (v43 >> 25) ^ 0x1FE8AA69A4LL));
v51 = v43 ^ ((v46 ^ 0x6FF1D8E87CF1679BLL) + 0x3103D94C869370C7LL * __ROR8__(v44, 28));
v52 = v44 + 0x1400BC8318A229E1LL * ((v47 >> 21) ^ __ROL8__(v45 - 0x24F81C87707E220BLL, 17));
v53 = v45 ^ ((v49 >> 28) + __ROR8__(v46 - 0x27A1132B6279476BLL, 6));
v54 = v46 + ((v48 << 35) ^ ((v47 << 50) ^ 0x5E6C000000000000LL | (v47 >> 14) ^ 0x77C01B754BB9LL));
v55 = v49 ^ ((v51 >> 31) + __ROL8__(v48 + 0x63D6FA204B4CCBCFLL, 10));
v56 = v47 - 0x3DE77B2DC411B6D7LL * ((v50 >> 24) ^ __ROL8__(v49 - 0x4A15270858F43151LL, 10));
v57 = v48 + ((8 * v52) ^ ((v50 << 43) ^ 0x62D9080000000000LL | (v50 >> 21) ^ 0xC8EC79B09LL));
v58 = v50 ^ ((v53 ^ 0xE1EDCC2EE5877CF5uLL) + 0x7F64B1454043C031LL * __ROL8__(v51, 32));
v59 = v52 ^ ((v56 >> 24) + __ROR8__(v53 + 0x1B2B41E896817A3FLL, 10));
v60 = v51 - 0x4EA9B0FEA2E8677LL * ((v54 >> 17) ^ __ROL8__(v52 - 0x51BAF62164C74AE7LL, 21));
v61 = v53 + ((v55 << 31) ^ ((v54 << 54) ^ 0x7FC0000000000000LL | (v54 >> 10) ^ 0xE9E64A1DBA285LL));
v62 = v54 ^ ((v57 ^ 0x3A7D3AE4AB6D0135LL) - 0x71F42BDEAD0266DFLL * __ROL8__(v56, 21));
v63 = v56 ^ ((v58 >> 27) + __ROL8__(v55 + 0x716AA0D9BEDF27E7LL, 6));
v64 = v55 + ((v60 << 34) ^ ((v57 << 47) ^ 0xD866800000000000uLL | (v57 >> 17) ^ 0x7CBB7403E95ALL));
v65 = v57 ^ ((v59 ^ 0x75B810328A55E935LL) - 0x53D54AFAC9AB2C5DLL * __ROL8__(v58, 28));
v66 = v60 ^ ((v62 >> 20) + __ROR8__(v59 - 0x7866DC2A6617E8B3LL, 14));
v67 = v58 + 0x392944951288DDD5LL * ((v61 >> 13) ^ __ROL8__(v60 - 0x8271C7F0322587LL, 25));
v68 = v59 + ((v63 << 27) ^ ((v61 << 58) ^ 0x4400000000000000LL | (v61 >> 6) ^ 0x3501C909EFA44DELL));
v69 = v61 ^ ((v64 ^ 0xADD987FBCFBC8789uLL) + 0x274B8B151001D861LL * __ROL8__(v62, 17));
v70 = v62 - 0x6786B939D3163E71LL * ((v65 >> 6) ^ __ROR8__(v63 - 0x42C9207CB1A3B36BLL, 28));
v71 = v63 + ((v67 << 30) ^ ((v64 << 51) ^ 0x8E98000000000000uLL | (v64 >> 13) ^ 0x4C0A33F8E0E1ALL));
v72 = v64 ^ ((v66 ^ 0x44C3B37C8A96B51FLL) + 0x123ABCC4703757F7LL * __ROL8__(v65, 24));
v73 = v65 - 0x1DB16CC1B0E6F585LL * ((v68 >> 9) ^ __ROL8__(v67 + 0x2BA0E13EA5C89655LL, 29));
v74 = v67 ^ ((v69 >> 16) + __ROR8__(v66 + 0x10DC33DFAAB00B75LL, 18));
v75 = v66 + ((v70 << 23) ^ ((v68 << 7) ^ 0x4AEFE328444AD980LL | (v68 >> 57) ^ 0x13));
v76 = v68 ^ ((v71 ^ 0xFF6D82A6AC000253uLL) - 0x41CFACFCBAD2C979LL * __ROL8__(v69, 13));
v77 = v69 + 0x2BB30A72DE0818AFLL * ((v72 >> 37) ^ __ROR8__(v70 + 0x5E667C9420146559LL, 24));
v78 = v70 ^ ((v73 >> 9) + __ROR8__(v71 + 0x55DB5764DAA4BB3DLL, 29));
v79 = v71 ^ ((v74 ^ 0xD3628CD7A909292FuLL) + 0x7E7ADD40C19AAC93LL * __ROL8__(v72, 20));
v80 = v72 - 0x724D65335DF1CD57LL * ((v75 >> 5) ^ __ROR8__(v73 - 0x222BFE9FE544FCA3LL, 31));
v81 = v73 ^ ((v76 >> 12) + __ROR8__(v74 + 0x2CCE39514AC15261LL, 22));
v82 = v74 + ((v77 << 19) ^ ((v75 << 11) ^ 0x79D5A4B4C2D0B800LL | (v75 >> 53) ^ 0x268));
v83 = v75 ^ ((v78 ^ 0xE2CA964167201681uLL) - 0x1E3B37B554EFB64FLL * __ROL8__(v76, 9));
v84 = v76 + 0x54564FEEED4A2551LL * ((v79 >> 33) ^ __ROR8__(v77 - 0x5046941559EAB715LL, 20));
v85 = v77 ^ ((v80 >> 5) + __ROL8__(v78 - 0x68A74482835F3C4FLL, 31));
v86 = v78 + ((v81 << 12) ^ ((v79 << 22) ^ 0xCF104A9F2C00000LL | (v79 >> 42) ^ 0xAE70A));
v87 = v80 ^ ((v83 >> 8) + __ROR8__(v81 - 0x441506D21F4A433BLL, 26));
v88 = v79 + 0x466E860D0FF31427LL * ((v82 >> 36) ^ __ROR8__(v80 - 0x1A23B4649024AD97LL, 27));
v89 = v81 + ((v84 << 15) ^ ((v82 << 15) ^ 0xD007C9E47B838000uLL | (v82 >> 49) ^ 0xF3B));
v90 = v82 ^ ((v85 ^ 0xB967E40D850BCBA3uLL) + 0xD33663913B85E0BLL * __ROL8__(v83, 5));
v91 = v84 ^ ((v88 >> 36) + __ROL8__(v85 + 0x686C6D7BEAEEE42BLL, 27));
v92 = v83 - 0x1C1E0C294C803A33LL * ((v86 >> 29) ^ __ROR8__(v84 - 0x18413DF55DC482BBLL, 16));
v93 = v85 + ((v87 << 8) ^ ((v86 << 26) ^ 0xE052D2B2CC000000uLL | (v86 >> 38) ^ 0x183F941));
v94 = ((v89 ^ 0x6AD66761CA8D46DFLL) + 0x67A29D2AE791698BLL * __ROR8__(v88, 15)) ^ v86;
v95 = v88 ^ ((v90 >> 4) + __ROR8__(v87 + 0x7DAA0738F41FFEFDLL, 30));
v96 = v87 + ((v92 << 11) ^ ((v89 << 19) ^ 0x65F7116B0EB80000LL | (v89 >> 45) ^ 0x2182));
v97 = v89 ^ ((v91 ^ 0x8DD5AA43B159CA6DuLL) + 0x44F5D4BDAB04962FLL * __ROR8__(v90, 8));
v98 = v92 ^ (HIDWORD(v94) + __ROL8__(v91 - 0x56042A15CF52A2D3LL, 23));
v99 = v90 + 0x6BDED3FD9663DC43LL * ((v93 >> 25) ^ __ROR8__(v92 + 0x17D9E1FC97627321LL, 12));
v100 = v91 + ((16 * v95) ^ ((v93 << 30) ^ 0x3B19C144C0000000LL | (v93 >> 34) ^ 0x34A2DF12));
v101 = v93 ^ ((v96 ^ 0x41EDB51CF17DAB83LL) + 0x524CACD6A49B9237LL * __ROR8__(v94, 19));
v102 = v94 + 0x53D05B8B7CBC8017LL * ((v97 >> 18) ^ __ROL8__(v95 - 0x724ABB70191820C7LL, 8));
v103 = v95 + ((v99 << 7) ^ ((v96 << 23) ^ 0xDDEB337AEA800000uLL | (v96 >> 41) ^ 0x1392F9));
v104 = v96 ^ ((v98 ^ 0xCBA8229652C78F8BuLL) - 0x1D100DDFB91144BFLL * __ROR8__(v97, 12));
v105 = v97 + 0x38320293A369F7DBLL * ((v100 >> 21) ^ __ROR8__(v99 - 0x24135E93B3A718FBLL, 8));
v106 = v99 ^ ((v101 >> 28) + __ROL8__(v98 + 0x27E4300E1006E54DLL, 19));
v107 = v98 + ((v102 << 35) ^ ((v100 << 34) ^ 0x7F9F32FC00000000LL | (v100 >> 30) ^ 0x32C47B367LL));
v108 = v100 ^ ((v103 ^ 0xBD0672989896ADD7uLL) + 0x4DF074EB474A506BLL * __ROR8__(v101, 23));
v109 = v101 - 0x5BC114B10B45A0B9LL * ((v104 >> 14) ^ __ROL8__(v102 + 0x5CF6F08E031F85BDLL, 12));
v110 = v102 ^ ((v105 >> 21) + __ROL8__(v103 + 0x6007DC6C8DBFCC6DLL, 8));
v111 = v103 ^ ((v106 ^ 0xF832DBB01D7235A3uLL) + 0x6DF7503ECB23D8BDLL * __ROR8__(v104, 16));
v112 = v105 ^ ((v108 >> 24) + __ROL8__(v106 + 0x7B989A80E3463501LL, 15));
v113 = v104 + 0x31C6B0E412439BE9LL * ((v107 >> 17) ^ __ROL8__(v105 - 0x7AECF4BC357F17F1LL, 5));
v114 = v106 + ((v109 << 31) ^ ((v107 << 38) ^ 0x273063C000000000LL | (v107 >> 26) ^ 0x21D51AF686LL));
v115 = v107 ^ ((v110 ^ 0x296D55015EEF7C79LL) - 0x567817B8E800CD13LL * __ROR8__(v108, 27));
v116 = v108 - 0x6D033D7C0B08892BLL * ((v111 >> 10) ^ __ROL8__(v109 - 0x341BD58FF744A139LL, 16));
v117 = v109 ^ ((v113 >> 17) + __ROR8__(v110 + 0x5DC03E25944143D3LL, 5));
v118 = v110 + ((v112 << 24) ^ ((v111 << 49) ^ 0xA5B6000000000000uLL | (v111 >> 15) ^ 0x1055EC57416E2LL));
v119 = v113 ^ ((v115 >> 20) + __ROL8__(v112 - 0xDF27BBAD3B71725LL, 11));
v120 = v111 + 0x6F8593568B10BD89LL * ((v114 >> 13) ^ __ROL8__(v113 - 0x3197E91A03944E01LL, 9));
v121 = v112 + ((v116 << 27) ^ ((v114 << 42) ^ 0x6797540000000000LL | (v114 >> 22) ^ 0x27E58DF5F54LL));
v122 = v114 ^ ((v117 ^ 0xCC0DDAA68126764DuLL) - 0x45EE61AC6452AA19LL * __ROR8__(v115, 31));
v123 = v116 ^ ((v120 >> 13) + __ROR8__(v117 - 0x73AA50E88F59E711LL, 9));
v124 = v115 - 0x1EB822247C54AD19LL * ((v118 >> 6) ^ __ROL8__(v116 - 0x4950EACD1911C5DLL, 20));
v125 = v117 + ((v119 << 20) ^ ((v118 << 53) ^ 0xF9A0000000000000uLL | (v118 >> 11) ^ 0x11138414182FB5LL));
v126 = v118 ^ ((v121 ^ 0x54BA97AC147477ADLL) + 0x6B19818C073977B3LL * __ROL8__(v120, 22));
v127 = v120 ^ ((v122 >> 16) + __ROL8__(v119 + 0x3AA50910BF83BD2BLL, 7));
v128 = v119 + ((v124 << 23) ^ ((v121 << 46) ^ 0x6532C00000000000LL | (v121 >> 18) ^ 0x2A94E7962D8LL));
v129 = v121 ^ ((v123 ^ 0x9DF638CB3685BC8FuLL) + 0x281CF62DCC142853LL * __ROL8__(v122, 29));
v130 = v124 ^ ((v126 >> 9) + __ROR8__(v123 + 0x35CE27049C461B43LL, 13));
v131 = v122 + 0x3B66916FBA3E53B3LL * ((v125 >> 37) ^ __ROL8__(v124 + 0x17D8F9B57B34C69DLL, 24));
v132 = v123 + ((v127 << 16) ^ ((v125 << 57) ^ 0x9E00000000000000uLL | (v125 >> 7) ^ 0x17AE7A59CBB65A8LL));
v133 = v125 ^ ((v128 ^ 0x146E91343EF65613LL) + 0xD8F65CA5E10AD9DLL * __ROL8__(v126, 18));
v134 = v126 + 0x4CCB2F60093E3AEDLL * ((v129 >> 30) ^ __ROR8__(v127 + 0x27424735BC3509A3LL, 29));
v135 = v127 + ((v131 << 19) ^ ((v128 << 50) ^ 0x60CC000000000000LL | (v128 >> 14) ^ 0x149ECB988C5E8LL));
v136 = v128 ^ ((v130 ^ 0xD2839EB0BF82D103uLL) - 0x5D7EE79BF97E797DLL * __ROL8__(v129, 25));
v137 = v129 - 0x69B73B3D381E319FLL * ((v132 >> 33) ^ __ROL8__(v131 - 0x6ED451BC34BFCA9DLL, 28));
v138 = v131 ^ ((v133 >> 5) + __ROR8__(v130 + 0x418AB9A6311558FDLL, 17));
v139 = v130 + ((v134 << 12) ^ ((v132 << 6) ^ 0xC8916E4427FE3C0LL | (v132 >> 58) ^ 0xD));
v140 = v132 ^ ((v135 ^ 0x95406CFB40ED64A1uLL) + 0x774A71E594C00B19LL * __ROL8__(v133, 14));
v141 = v133 + 0x2F37322C7CFE1D3DLL * ((v136 >> 26) ^ __ROR8__(v134 - 0x76AA227B839F7FD1LL, 25));
v142 = v134 ^ ((v137 >> 33) + __ROR8__(v135 - 0x1D7B19EC730FBE11LL, 28));
v143 = v135 ^ ((v138 ^ 0xBD2628AC44526E99uLL) + 0x615C50F384A4023DLL * __ROL8__(v136, 21));
v144 = v136 + 0x2D8DE0F641EF87E3LL * ((v139 >> 29) ^ __ROL8__(v137 + 0x74D83C5A378AD2D1LL, 32));
v145 = v137 ^ ((v140 >> 36) + __ROR8__(v138 + 0x50C27569028F2329LL, 21));
v146 = v138 + ((v141 << 8) ^ ((v139 << 10) ^ 0x81A57ED3978FCC00uLL | (v139 >> 54) ^ 0x1EC));
v147 = v139 ^ ((v142 ^ 0x876DF688FDBF76D7uLL) - 0x6098BB67E5F06A3FLL * __ROL8__(v140, 10));
v148 = v140 + 0x6C9ED6231F90F283LL * ((v143 >> 22) ^ __ROR8__(v141 + 0x2DDC46424B6C79FDLL, 21));
v149 = v141 ^ ((v144 >> 29) + __ROL8__(v142 - 0x7578ECA2ABDE6215LL, 32));
v150 = v142 + ((v145 << 36) ^ ((v143 << 21) ^ 0xF7309C3A13600000uLL | (v143 >> 43) ^ 0x14E8CC));
v151 = v143 + 0x39BC5AB9C4DFC571LL * ((v146 >> 25) ^ __ROR8__(v144 + 0x783B20D3FC59E5BFLL, 28));
v152 = v144 ^ (HIDWORD(v147) + __ROR8__(v145 - 0x10279E87ED1490EDLL, 25));
v153 = v145 + ((16 * v148) ^ ((v146 << 14) ^ 0x9BD840149B904000uLL | (v146 >> 50) ^ 0x2638));
qword_20260010 = (v147 + 0x17DDA8E078EC6DF3LL * ((v150 >> 18) ^ __ROR8__(v148 - 0x4EA5A86F2DFD0081LL, 17))) ^ v153 ^ v151 ^ (v149 + ((v152 << 32) ^ ((v150 << 25) ^ 0xA7995752C2000000uLL | (v150 >> 39) ^ 0x1E51517)));
result = ((v153 ^ 0x3EE9030A33EC6169LL) + 0x7C1624D2D1AC7C93LL * __ROR8__(v151, 14)) ^ ((v151 >> 25)
+ __ROL8__(
v149 - 0x312EA921524D0D33LL,
28)) ^ v152 ^ v148 ^ v150 ^ v146 ^ ((v149 ^ 0xB3661EE145B34D45uLL) + 0x30139D7A38219B07LL * __ROL8__(v147, 6));
qword_20260018 = result;
return result;
}
So after inspecting the code, I noticed there is a 16-byte buffer produced at the end of this function at *0x20260010
This shall be an important value, so I grabbed it

D4 BA 32 97 22 D3 63 73 B6 99 EB 54 23 FB F0 B7
I was stuck here for a while trying to figure out with x64dbg why it’s not working and kept freezing like it’s was in an infinite loop
I know there was a trap to the debugger but with scyllahide I shall be okay right?
I searched in reference for the “Correct Flag” appearance to see where shall be the last checks and this is the function that have it
After some analyze to it, I found it compares something at 0x20260028 with 0x20260110
Bytes at 0x20260110 are the ones from Tlscallback_1 which likely the encrypted flag
So I have the bytes at 0x20260028 and the 16 bytes recovered from the large function we counter previously
D4 BA 32 97 22 D3 63 73 B6 99 EB 54 23 FB F0 B7
So I opened cyberchef and stared playing with what I got till I found this


This means we got the flag finally
Adding the correct enrypted flag instead of the dumy one and changing the mode to ECB (here is the recipe)
8A438883026EBC2CF0832C9AEDCCB45DAD2D868A3A986118B5EB98FA8FEA3193B0DD388135039458B6139D54A6A9503E
I got the flag
CATF{N0w_y0u_r34lly_f0und_th3_fl4g}
Devs beginning
After loading the evidence into FTK Imager and checking ConsoleHost_history.txt
at C:\Users\7asabo\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt:
The challenge idea stode out

Add-Type -AssemblyName System.Security
$keyBytes = [System.IO.File]::ReadAllBytes("C:\Users\7asbo\Desktop\confidential_repo_key")
$encrypted = [System.Security.Cryptography.ProtectedData]::Protect(`
$keyBytes,`
$null,`
[System.Security.Cryptography.DataProtectionScope]::CurrentUser`
)
Add-Type -AssemblyName System.Security
$keyBytes = [System.IO.File]::ReadAllBytes("C:\Users\7asabo\Desktop\confidential_repo_key")
$encrypted = [System.Security.Cryptography.ProtectedData]::Protect(`
$keyBytes,`
$null,`
[System.Security.Cryptography.DataProtectionScope]::CurrentUser`
)
cd .\Desktop\
mkdir project
cd .\project\
git init
echo "myrepo:git@github.com:7asabo16/confidential.git">private repo.txt
ls
echo "1-prepare a presentation about The Internship, 2-do the final project of the internship, 3-get ready for the new semester">Tasks.txt
cd ..
.\sdelete64.exe -p 3 -s C:\Users\7asabo\Desktop\project\ssh_key
.\sdelete64.exe -p 3 -s C:\Users\7asabo\Desktop\project\ssh_key.dpapi
cd .\project\
git add .\ssh_key.dpapi
git commit -m "back up key"
git config --global user.email "Eng7asabo12@outlook.com"
git config --global user.name "Eng7asabo"
git log
git commit -m "back up key"
ls
git add '.\private repo.txt'
git commit -m "dont share!!!"
ls
cd ..
.\sdelete64.exe -p3 '.\project\private repo.txt' .\project\ssh_key.dpapi
.\sdelete64.exe -p3 .\project\ssh_key.dpapi
.\sdelete64.exe -p3 C:\Users\7asabo\Desktop\project\ssh_key.dpapi
.\sdelete64.exe -p 3 C:\Users\7asabo\Desktop\project\ssh_key.dpapi
.\sdelete64.exe -p 3 'C:\Users\7asabo\Desktop\project\private repo.txt'
cd .\project\
ls
cd .\Desktop\
.\sdelete64.exe -p 3 .\script.ps1
So after checking the user 7asabo’s Desktop folder, I found a folder called project, and inside it was a Tasks.txt file and a .git folder

After navigating to the project’s repository directory and checking the history, I found the two committed objects:

Then I extracted them:
Now I had both the private repo URL myrepo:git@github.com:7asabo16/confidential.git and the ssh_key.dpapi file
We need to see what was inside that private repo, but to clone it we need access to it
Luckily we have ssh_key.dpapi, but it’s encrypted with 7asabo’s DPAPI MasterKey. To decrypt it, we need the master key located at C:\Users\7asabo\AppData\Roaming\Microsoft\Protect\S-1-5-21-1119876999-3187066875-582830896-1000 with MasterKey GUID 7abda562-0278-4847-81df-d4ce039112ae
To do so, I used Mimikatz:
.#####. mimikatz 2.2.0 (x64) #19041 Sep 19 2022 17:44:08
.## ^ ##. "A La Vie, A L'Amour" - (oe.eo)
## / \ ## /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
## \ / ## > https://blog.gentilkiwi.com/mimikatz
'## v ##' Vincent LE TOUX ( vincent.letoux@gmail.com )
'#####' > https://pingcastle.com / https://mysmartlogon.com ***/
mimikatz # privilege::debug
ERROR kuhl_m_privilege_simple ; RtlAdjustPrivilege (20) c0000061
mimikatz # lsadump::sam /sam:C:\Users\Flare\Desktop\Evidence-extracted\SAM /system:C:\Users\Flare\Desktop\Evidence-extracted\SYSTEM
Domain : WIN-37DI7ORCIS9
SysKey : 156b633468c51f81335669a61fb6d07f
Local SID : S-1-5-21-1119876999-3187066875-582830896
SAMKey : f2520e176678d6e9020dbc380ef9ff9d
RID : 000001f4 (500)
User : Administrator
RID : 000001f5 (501)
User : Guest
RID : 000001f7 (503)
User : DefaultAccount
RID : 000001f8 (504)
User : WDAGUtilityAccount
Hash NTLM: 0832c6325672f2a9c820660c2399e205
Supplemental Credentials:
* Primary:NTLM-Strong-NTOWF *
Random Value : 68b06b65630a84b4df542741857ce4ea
* Primary:Kerberos-Newer-Keys *
Default Salt : WDAGUtilityAccount
Default Iterations : 4096
Credentials
aes256_hmac (4096) : c8bef75f2e4764056eabe5fdbf781e4d60b25f0a9de291f0cfa0bb41731a49eb
aes128_hmac (4096) : c33f6b2600aff414dab566d3becd2aa2
des_cbc_md5 (4096) : d9dcb54543f14580
* Packages *
NTLM-Strong-NTOWF
* Primary:Kerberos *
Default Salt : WDAGUtilityAccount
Credentials
des_cbc_md5 : d9dcb54543f14580
RID : 000003e8 (1000)
User : 7asabo
Hash NTLM: 068689e40a6b94bc3d431b454c18d6b9
Supplemental Credentials:
* Primary:NTLM-Strong-NTOWF *
Random Value : f85c44c227ea916c9f3b1a2ccf173a7e
* Primary:Kerberos-Newer-Keys *
Default Salt : WIN-37DI7ORCIS97asabo
Default Iterations : 4096
Credentials
aes256_hmac (4096) : 06a9f9352e54952864bdc15ee71cc82e5595b52c7b5f285c2e0089bee9679cb3
aes128_hmac (4096) : 97f106a6500672d2ae8eef4b77699725
des_cbc_md5 (4096) : 8f6101e61008153e
* Packages *
NTLM-Strong-NTOWF
* Primary:Kerberos *
Default Salt : WIN-37DI7ORCIS97asabo
Credentials
des_cbc_md5 : 8f6101e61008153e
Doing so we have recovered
7asabo's Hash NTLM: 068689e40a6b94bc3d431b454c18d6b9
Now let’s dump the Dpapi key
mimikatz # lsadump::secrets /security:C:\Users\Flare\Desktop\Evidence-extracted\SECURITY /system:C:\Users\Flare\Desktop\Evidence-extracted\SYSTEM
Domain : WIN-37DI7ORCIS9
SysKey : 156b633468c51f81335669a61fb6d07f
Local name : WIN-37DI7ORCIS9 ( S-1-5-21-1119876999-3187066875-582830896 )
Domain name : WORKGROUP
Policy subsystem is : 1.18
LSA Key(s) : 1, default {4e377552-0f30-c69b-39b6-a11b34f71090}
[00] {4e377552-0f30-c69b-39b6-a11b34f71090} 5a2dcc13d055a7cd3feed2acb8d13edca277ceed1c1f937f4001e607d3c69e29
Secret : DefaultPassword
cur/text: mysecretpassword
Secret : DPAPI_SYSTEM
cur/hex : 01 00 00 00 9f 94 65 f6 1c b6 0d 78 8b 8c 2d b3 50 f9 c5 a8 ca bf b4 1b c1 d9 53 3c b3 29 e0 fd d7 30 13 d8 83 9a c9 58 4c 2e f1 f8
full: 9f9465f61cb60d788b8c2db350f9c5a8cabfb41bc1d9533cb329e0fdd73013d8839ac9584c2ef1f8
m/u : 9f9465f61cb60d788b8c2db350f9c5a8cabfb41b / c1d9533cb329e0fdd73013d8839ac9584c2ef1f8
old/hex : 01 00 00 00 ba 5f ed ef 3e 11 4d ef f7 09 73 33 f1 de e9 18 8b f3 63 0f a0 64 66 d8 80 ef 6d e7 78 a3 6f e7 2c e4 29 0a 78 61 7b 10
full: ba5fedef3e114deff7097333f1dee9188bf3630fa06466d880ef6de778a36fe72ce4290a78617b10
m/u : ba5fedef3e114deff7097333f1dee9188bf3630f / a06466d880ef6de778a36fe72ce4290a78617b10
Secret : NL$KM
cur/hex : 45 7b 4c 6d 84 97 4e c8 d6 6e 44 9f 08 97 88 b6 ea 05 27 59 90 44 da f1 4e 19 d1 30 dc c8 2d 45 97 cb 49 9d 0c c4 10 5c 96 9e 41 37 8e dc d2 61 71 e2 92 74 03 b3 c4 38 77 6b 29 ea 8a 1b 1b 45
old/hex : 45 7b 4c 6d 84 97 4e c8 d6 6e 44 9f 08 97 88 b6 ea 05 27 59 90 44 da f1 4e 19 d1 30 dc c8 2d 45 97 cb 49 9d 0c c4 10 5c 96 9e 41 37 8e dc d2 61 71 e2 92 74 03 b3 c4 38 77 6b 29 ea 8a 1b 1b 45
mimikatz #
Doing so we have recovered the Dpapi keys for machine and user ` m/u : 9f9465f61cb60d788b8c2db350f9c5a8cabfb41b / c1d9533cb329e0fdd73013d8839ac9584c2ef1f8`
and the user password mysecretpassword
Now to find the master key we need, we can use either of those approches:
dpapi::masterkey /in:"C:\Users\Flare\Desktop\Evidence-extracted\7abda562-0278-4847-81df-d4ce039112ae" /sid:S-1-5-21-1119876999-3187066875-582830896-1000 /password:mysecretpassword
Or using the NTLM hash
dpapi::masterkey /in:"C:\Users\Flare\Desktop\Evidence-extracted\7abda562-0278-4847-81df-d4ce039112ae" /sid:S-1-5-21-1119876999-3187066875-582830896-1000 /hash:068689e40a6b94bc3d431b454c18d6b9
And this is what we got
mimikatz # dpapi::masterkey /in:"C:\Users\Flare\Desktop\Evidence-extracted\7abda562-0278-4847-81df-d4ce039112ae" /sid:S-1-5-21-1119876999-3187066875-582830896-1000 /hash:068689e40a6b94bc3d431b454c18d6b9
**MASTERKEYS**
dwVersion : 00000002 - 2
szGuid : {7abda562-0278-4847-81df-d4ce039112ae}
dwFlags : 00000005 - 5
dwMasterKeyLen : 000000b0 - 176
dwBackupKeyLen : 00000090 - 144
dwCredHistLen : 00000014 - 20
dwDomainKeyLen : 00000000 - 0
[masterkey]
**MASTERKEY**
dwVersion : 00000002 - 2
salt : 951c4b1f1384b238979fd099429a957b
rounds : 00001f40 - 8000
algHash : 0000800e - 32782 (CALG_SHA_512)
algCrypt : 00006610 - 26128 (CALG_AES_256)
pbKey : a07b2563fd708c7feeb3da07bf3142680e1919f829d4ac6287cf808618ac3df662151f92db36f08e58f9b49d752eac78c98dd2cb7bea3cd381f8d65bfaf88b402e25ccf28353db89dbe841a7ea9e736f657c040d17b9f2db026ed23c20c0b35eda4090c417cc94bc0f8ed07898063fe7912548eb9254917ef68740fb70d654145ec5741f0c21e3345987280e53eb6952
[backupkey]
**MASTERKEY**
dwVersion : 00000002 - 2
salt : c3facbc2b0be497e7cba300033e22bf3
rounds : 00001f40 - 8000
algHash : 0000800e - 32782 (CALG_SHA_512)
algCrypt : 00006610 - 26128 (CALG_AES_256)
pbKey : b84b3da09b24dfb0344356d4c2a97a3e2cae8825f2052c85f89778673b2869da92d5d4e8ff4851ec874f5802023d953a117b16df918bec357451699bd6f3814628f93bd35a221d34203ab191cccf0cf8b009e4800e6f40a60e15e30d72bc629ce64a80a13503332edf5eef699982a11b
[credhist]
**CREDHIST INFO**
dwVersion : 00000003 - 3
guid : {aa0b5148-71c0-437d-b001-a1f374a762dc}
[masterkey] with volatile cache: SID:S-1-5-21-1119876999-3187066875-582830896-1000;GUID:{aa0b5148-71c0-437d-b001-a1f374a762dc};MD4:068689e40a6b94bc3d431b454c18d6b9;SHA1:a5f1e811f41d623b4580aee922b5ee85df8627a2;
key : bc5a44b02fb17f6016294151a81b754ff0505d633d1caafe2f2090c8f5495ce9595c8433da39ec12d73abe328e840974f20d61594023ea73daa8aa8e6ec80224
sha1: b2a5761871ba49fefca4e0e59ded1cd8c7da6629
[masterkey] with hash: 068689e40a6b94bc3d431b454c18d6b9 (ntlm type)
ERROR kuhl_m_dpapi_masterkey ; kull_m_dpapi_unprotect_masterkey_with_userHash
mimikatz # dpapi::masterkey /in:"C:\Users\Flare\Desktop\Evidence-extracted\7abda562-0278-4847-81df-d4ce039112ae" /sid:S-1-5-21-1119876999-3187066875-582830896-1000 /password:mysecretpassword
**MASTERKEYS**
dwVersion : 00000002 - 2
szGuid : {7abda562-0278-4847-81df-d4ce039112ae}
dwFlags : 00000005 - 5
dwMasterKeyLen : 000000b0 - 176
dwBackupKeyLen : 00000090 - 144
dwCredHistLen : 00000014 - 20
dwDomainKeyLen : 00000000 - 0
[masterkey]
**MASTERKEY**
dwVersion : 00000002 - 2
salt : 951c4b1f1384b238979fd099429a957b
rounds : 00001f40 - 8000
algHash : 0000800e - 32782 (CALG_SHA_512)
algCrypt : 00006610 - 26128 (CALG_AES_256)
pbKey : a07b2563fd708c7feeb3da07bf3142680e1919f829d4ac6287cf808618ac3df662151f92db36f08e58f9b49d752eac78c98dd2cb7bea3cd381f8d65bfaf88b402e25ccf28353db89dbe841a7ea9e736f657c040d17b9f2db026ed23c20c0b35eda4090c417cc94bc0f8ed07898063fe7912548eb9254917ef68740fb70d654145ec5741f0c21e3345987280e53eb6952
[backupkey]
**MASTERKEY**
dwVersion : 00000002 - 2
salt : c3facbc2b0be497e7cba300033e22bf3
rounds : 00001f40 - 8000
algHash : 0000800e - 32782 (CALG_SHA_512)
algCrypt : 00006610 - 26128 (CALG_AES_256)
pbKey : b84b3da09b24dfb0344356d4c2a97a3e2cae8825f2052c85f89778673b2869da92d5d4e8ff4851ec874f5802023d953a117b16df918bec357451699bd6f3814628f93bd35a221d34203ab191cccf0cf8b009e4800e6f40a60e15e30d72bc629ce64a80a13503332edf5eef699982a11b
[credhist]
**CREDHIST INFO**
dwVersion : 00000003 - 3
guid : {aa0b5148-71c0-437d-b001-a1f374a762dc}
[masterkey] with password: mysecretpassword (normal user)
key : bc5a44b02fb17f6016294151a81b754ff0505d633d1caafe2f2090c8f5495ce9595c8433da39ec12d73abe328e840974f20d61594023ea73daa8aa8e6ec80224
sha1: b2a5761871ba49fefca4e0e59ded1cd8c7da6629
And to recover the encrypted ssh_key we can use mimikatz
dpapi::blob /in:"C:\Users\Flare\Desktop\Evidence-extracted\recovered_ssh_key.dpapi" /masterkey:bc5a44b02fb17f6016294151a81b754ff0505d633d1caafe2f2090c8f5495ce9595c8433da39ec12d73abe328e840974f20d61594023ea73daa8aa8e6ec80224
The output was
mimikatz # dpapi::blob /in:"C:\Users\Flare\Desktop\Evidence-extracted\recovered_ssh_key.dpapi" /masterkey:bc5a44b02fb17f6016294151a81b754ff0505d633d1caafe2f2090c8f5495ce9595c8433da39ec12d73abe328e840974f20d61594023ea73daa8aa8e6ec80224
**BLOB**
dwVersion : 00000001 - 1
guidProvider : {df9d8cd0-1501-11d1-8c7a-00c04fc297eb}
dwMasterKeyVersion : 00000001 - 1
guidMasterKey : {7abda562-0278-4847-81df-d4ce039112ae}
dwFlags : 00000000 - 0 ()
dwDescriptionLen : 00000002 - 2
szDescription :
algCrypt : 00006610 - 26128 (CALG_AES_256)
dwAlgCryptLen : 00000100 - 256
dwSaltLen : 00000020 - 32
pbSalt : 5b4a574aeba9a2c26c7054dd24a450547540085abe527a4b220e634e4ee5da51
dwHmacKeyLen : 00000000 - 0
pbHmackKey :
algHash : 0000800e - 32782 (CALG_SHA_512)
dwAlgHashLen : 00000200 - 512
dwHmac2KeyLen : 00000020 - 32
pbHmack2Key : 14cffba6778962bad37cdb9c5ebcdd05937f701d8bd0c881c08286b9b240e8af
dwDataLen : 000001a0 - 416
pbData : a06228d0f2987b917891a0c928aa82d6a77331227468377af5246039cbd148d18bfcffdca50be55eaf050931d50bec0495a548ef990cee1dc905e6365eba68e94d1cde58c83a4fe8d2c27565f53ce291afcff7bb73daa8e7c1ac2a5dcc6eede77b3c116c14c85758665ae9b58c421d529ce2d295f142b07f3c36e6b98daaec9a2be4df8f210a23cc28f8f1b6fbca8fefe4fe6f4f3a37a6937abe0fe9a54b05f46d660d149536c5c5f3091a5d1e1687deec948df0a6d0e227ddbb38a49e3c489bb18ce8f2d21b13a8b968734e0911abe0da768eb8dc21d04c9fd406a70480bf01600b55f4a399bae8cbc1ef298b76437d165c2349b5b0bdb561340c1a313b5dc8218b5a505c97f3d6c873dddc94216aee86ee050cbb5308469e2873b8a461a554baf3c85f28a50b725fbe265da82f8251bac68d000a1c1edc51ab06f9a518ba8c223b9e23669f89737bc657432624e5ebec725cb53c24019563d76d9709892e9c2cffc0e30de786072a6de3df75161b84615d8d47f286517db059bcd2340e2dd3a98770a3f0abecfff90b03a7a67e7e7c35850786aac417161dac419b19f49a4b
dwSignLen : 00000040 - 64
pbSign : d5566d7fa247d36e081ed89a3e08c7e6f7d7a6583feec106faf4b8f4eae6960c5436ccc1a4760d866d16ca4ed5fe612c6623a2473479bfbd7b4f1fd29c6f6979
* volatile cache: GUID:{7abda562-0278-4847-81df-d4ce039112ae};KeyHash:b2a5761871ba49fefca4e0e59ded1cd8c7da6629;Key:available
* masterkey : bc5a44b02fb17f6016294151a81b754ff0505d633d1caafe2f2090c8f5495ce9595c8433da39ec12d73abe328e840974f20d61594023ea73daa8aa8e6ec80224
description :
data: 2d 2d 2d 2d 2d 42 45 47 49 4e 20 4f 50 45 4e 53 53 48 20 50 52 49 56 41 54 45 20 4b 45 59 2d 2d 2d 2d 2d 0a 62 33 42 6c 62 6e 4e 7a 61 43 31 72 5a 58 6b 74 64 6a 45 41 41 41 41 41 42 47 35 76 62 6d 55 41 41 41 41 45 62 6d 39 75 5a 51 41 41 41 41 41 41 41 41 41 42 41 41 41 41 4d 77 41 41 41 41 74 7a 63 32 67 74 5a 57 0a 51 79 4e 54 55 78 4f 51 41 41 41 43 42 77 34 46 36 4a 32 2b 7a 31 71 7a 50 4c 36 55 38 61 68 4c 74 48 57 47 33 68 79 71 77 50 32 55 30 4f 72 73 31 31 42 65 4d 52 37 67 41 41 41 4a 68 7a 72 5a 68 32 63 36 32 59 0a 64 67 41 41 41 41 74 7a 63 32 67 74 5a 57 51 79 4e 54 55 78 4f 51 41 41 41 43 42 77 34 46 36 4a 32 2b 7a 31 71 7a 50 4c 36 55 38 61 68 4c 74 48 57 47 33 68 79 71 77 50 32 55 30 4f 72 73 31 31 42 65 4d 52 37 67 0a 41 41 41 45 42 58 48 4d 7a 4f 31 62 58 4d 43 6b 76 65 68 78 74 68 65 53 74 48 35 49 73 75 68 4b 46 6a 61 52 2f 54 43 42 41 36 6b 44 45 41 50 33 44 67 58 6f 6e 62 37 50 57 72 4d 38 76 70 54 78 71 45 75 30 64 59 0a 62 65 48 4b 72 41 2f 5a 54 51 36 75 7a 58 55 46 34 78 48 75 41 41 41 41 45 31 52 6f 61 58 4d 67 63 6d 56 77 62 79 42 70 63 79 42 7a 5a 57 4e 79 5a 58 51 42 41 67 3d 3d 0a 2d 2d 2d 2d 2d 45 4e 44 20 4f 50 45 4e 53 53 48 20 50 52 49 56 41 54 45 20 4b 45 59 2d 2d 2d 2d 2d 0a
mimikatz #
Then I used CyberChef to save the private key so I could connect to the private repo with it

Then using this command
GIT_SSH_COMMAND='ssh -i "$PWD/private_key" -o IdentitiesOnly=yes' git clone git@github.com:7asabo16/confidential.git ./confidential_repo
I cloned the repo successfully and got the flag
CATF{Wh4t_A_G1t_Sk1ll_Y0u_H4v3}
Nightjar_FINWKS07
![]()
Questions and description
peration Nightjar (FINWKS07) - DFIR Investigation
==================================================
A workstation on the ARC corporate network was flagged after a network sensor
logged repeated failed name lookups originating from an accounting endpoint in
the small hours of the morning. The host was pulled and a triage image was
collected. Somewhere in this collection is a program that has no business being
where it is, wearing a coat that belongs to something else. It keeps its secrets
folded inside a file that was never meant to be read as text, wakes itself
without a single entry in the places you were taught to check first, and has
been calling home to a name that does not answer.
Nothing on this host tells you where to look. Public reporting exists on the
technique this intruder borrowed; it is not always correct, and it knows nothing
about this host, so trust it for nothing you can measure yourself. Work the
evidence, scope the intrusion end to end, and determine exactly what it did,
when, and how far it got.
Answer all questions to retrieve the flag.
Q1-One executable collected from this host is not what it claims to be. Give its SHA-256.
Q2-Give the full domain (including TLD) this host was configured to contact.
Q3-Give the exact URI path the implant uses to POST to that destination.
Q4-Give the name of the persistence entry the attacker registered on the host.
Q5-The implant runs once per day. How many minutes after local midnight is it set to trigger?
Q6-At what UTC time was the persistence installed on this host?
Q7-Give the exact size in bytes of the file that holds the command-and-control configuration.
Q8-Give the SHA-256 of that file.
Q9-When did the implant first execute on this host? Answer in UTC.
Q10-How many seconds elapsed between the implant's compilation and the staging of its configuration on this host?
Q11-How many times did the implant successfully reach its command-and-control destination?
Reading the prompt gives us several key clues:
- we are searching for a program that masquerading as something else
- It stores its C2 secrets inside a file not meant to be read as text
- It achieves persistence through a non-traditional method (“wakes itself without a single entry in the places you were taught to check first”)
We were provided with a triage collection containing the filesystem image and a Modules folder with parsed artifacts:
20260731061204_Amcache_UnassociatedFileEntries.csv
EvtxECmd_Output.csv
20260731061204_LECmd_Output.csv
20260731061204_FINWKS07_J_Output.csv
20260731061204_FINWKS07_MFT_Output.csv
20260731061204_PECmd_Output.csv
20260731061204_RECmd_Batch_Kroll_Autoruns_Output.csv
20260731061204_SrumECmd_NetworkUsages_Output.csv
Let’s solve the investigation questions one by one.
Q1: One executable collected from this host is not what it claims to be. Give its SHA-256.
Loading the triage image into FTK Imager and checking user activity, under khaled.allam’s Recent items we see RealtekAudioDriver.zip.lnk:

Navigating to C:\Program Files\Realtek\Audio\, we find an executable named RtkNGUI64.exe. While this filename typically corresponds to the legitimate Realtek Audio utility, inspecting the binary shows it is unsigned and flagged as malicious on VirusTotal

Its SHA-256 hash:
9b32b09bdde7d5749ac0ffadae4af2d04cdd237e95aa88fb700ab79b0e9ca804
Answer: 9b32b09bdde7d5749ac0ffadae4af2d04cdd237e95aa88fb700ab79b0e9ca804
There were some blogs discussing this incident, like this one, but as mentioned in the description, we shouldn’t rely blindly on them
Q2: Give the full domain (including TLD) this host was configured to contact.
We have two ways:
-
Triage Event Logs: Inspecting the Event Logs for
RtkNGUI64.exeactivity, I found:
This looks like an interesting persistence mechanism, and inspecting around it revealed a suspicious domain it was communicating with:

-
Reversing the malware: To recover where it hides its secrets. We find out that it stores its secrets inside
C:\ProgramData\desktop.iniin a hidden way. This confirms the C2 server address, and I will detail the reversing process in the next question.
Answer: trustedinstaler.com
Q3: Give the exact URI path the implant uses to POST to that destination.
Loading RtkNGUI64.exe in IDA Pro and inspecting its functions, I found a function that performs some encoding or obfuscating using a repeating 4-byte XOR key 13 37 AC 5E

Basically, it is a simple XOR on those bytes, so I extracted them and decoded them using this CyberChef recipe:
- Bytes at 0x4020:
7012DF7B60528C717017892D - Bytes at 0x402C:
3D54C333 - Bytes at 0x4030:
500DF00E6158CB2C725AE83F6756F03A7644C72A7C4782377D5E - Bytes at 0x4060:
5E58D6377F5BCD7126199C7E3B60C5307758DB2D3379F87E2207826E2817FB377D019865334F9A6A3A17ED2E635BC9097655E7376718996D24199F68331FE716477AE072335BC5357617EB3B705CC3773374C42C7C5AC971240F826E3D04956E27199D6E2B17FF3F7556DE373C029F693D049A - Bytes at 0x40D8:
660A892D355391307C59C9 - Bytes at 0x40F0:
3C43C932765AC92A614E833D7C5BC03B7043822E7B47
Deobfuscating these reveals:
c%s%se /c %s.comC:\ProgramData\desktop.iniMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.10u=%s&d=none/telemetry/collect.php
The implant sends HTTP POST requests to /telemetry/collect.php
Answer: /telemetry/collect.php
Q4: Give the name of the persistence entry the attacker registered on the host.
As the description states, the malware wakes itself without a single entry in the places you were taught to check first (no standard Run keys, Services, or Scheduled Tasks)
Earlier, while searching EvtxECmd_Output.csv for RtkNGUI64.exe, we encountered this persistence entry:
ESS namespace=//./root/subscription
Filter: __EventFilter.Name="RtkAudioSvc"
Consumer: CommandLineEventConsumer.Name="RtkAudioMon"
Query: SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_LocalTime' AND TargetInstance.Hour = 3 AND TargetInstance.Minute = 17
CommandLineTemplate: C:\Progra~1\Realtek\Audio\RtkNGUI64.exe
So the answer shall be
Answer: RtkAudioSvc
Q5: The implant runs once per day. How many minutes after local midnight is it set to trigger?
From the WMI query we found:

3 hours and 17 minutes translates to 197 minutes
Answer: 197
Q6: At what UTC time was the persistence installed on this host?

Answer: 2026-07-14 12:07:10
Q7: Give the exact size in bytes of the file that holds the command-and-control configuration.

Answer: 3602
Q8: Give the SHA-256 of that file.
Answer: a83f2ff251867a80954b4f99be8d52fb41107aa22d44dfd22a557402ba792f3c
Q9: When did the implant first execute on this host? Answer in UTC.
Checking 20260731061204_PECmd_Output.csv for RtkNGUI64.exe:

This process ran 6 times:
2026-07-15 00:17:04.00000002026-07-18 00:17:02.00000002026-07-22 00:17:05.00000002026-07-25 00:17:01.00000002026-07-28 00:17:06.00000002026-07-30 00:17:03.0000000
So the answer shall be
Answer: 2026-07-15 00:17:04
Q10: How many seconds elapsed between the implant’s compilation and the staging of its configuration on this host?
Inspecting the PE header to get its compilation timestamp:
The compilation time was 2026-07-14 09:22:41
Then i checked the MFT records to find the time creation of ‘C:\ProgramData\desktop.ini’:
The creation time was 2026-07-14 12:05:33
Calculating the difference
Answer: 9772
Q11: How many times did the implant successfully reach its command-and-control destination?
Checking network activity in 20260731061204_SrumECmd_NetworkUsages_Output.csv and other logs to see if it was able to reach trustedinstaler[.]com:

but it was never able to reach successfully, so the answer shall be
Answer: 0
After answering all 11 questions on the server successfully, we retrieve the flag:

CATF{b16e21785bcdd2394b3e2a77de0f8eaa}
Conclusion
Overall, it was a really nice journey and a memorable experience. I’m truly thankful for all my teammates and their hard work. A special thanks to the authors for the effort they put into creating all these awesome challenges.
اللهم انفعنا بما علَّمْتَنا، وعلِّمنا ما ينفعنا، وزدنا علمًا وفقهًا وفهمًا