In anticipation for the 2019 Flare-On Challenge by FireEye, the Flare team released a sample[1] at Blackhat Security Conference to hold us over until the challenge opens. Let's dive in for a warm-up.


Extracting "BHUSA2019.zip" provides a short "readme.txt" and "MemeCatBattlestation.exe" (MD5: aaeae4d6669c0bc286bfded44045be76). For these types of challenges, I typically jump straight into IDA before any dynamic analysis to better understand what I may be dealing with before moving forward. Fortunately, upon loading the PE file into IDA, IDA identifies the binary as a .NET assembly. .NET assemblies are often written in C# and can be decompiled directly into code.
JetBrains dotPeek [2] is my go to .Net decompiler. A beautiful UI and code highlighting makes analysis more fun. Decompiling "MemeCatBattlestation.exe", we begin to see several C# files:

  • Program.cs
  • Stage1Form.cs
  • Stage2Form.cs
  • Stage3Form.cs
  • BattleCatManagerInstance.cs

Let's begin walking through each of these based on the code flow.

Program

We begin here since the Main function is defined in this file. Brief review of this function shows instantiations of the three form files previously identified, starting with "Stage1Form".

Stage1Form

Stage1Form shows a typical C# Class definition. The method FireButton_Click reveals a peculiar conditional statement:

if (this.codeTextBox.Text == "RAINBOW")

Since it's the first stage, this simple cleartext flag is our first victory. Let's not take it for granted and move on. Continuing analysis of the code flow within the Main function, we find the next call to Stage2Form.

Stage2Form

Stage2Form appears similar in design to Stage1Form. Diving into method FireButton_Click we find a similar conditional statement:

if (this.isValidWeaponCode(this.codeTextBox.Text))

Analyzing the boolean method isValidWeaponCode, we find a character array set to the text provided in the textbox by the user. Next we find a basic for-loop:

for (int index = 0; index < length; ++index){
  charArray[index] ^= (char) (65 + index * 2);
}

This for-loop iterates over a character array and performs an XOR operation with the value 65 ('A') + the current index * 2. The following return statement invokes the SequenceEqual method on the character array with the hard-coded array defined inline:

{'\x0003', '"', '"', '"', '%', '\x0014', '\x000E', '.', '?', '=', ':', '9'}

We can take this hard-coded character array and use it with the for-loop to reverse the logic. The following Python script was my approach. Note, we'll have easier and cleaner code removing the unicode padding in the hard-coded array as I did below.

In [1]: secret = ['\x03', '"', '"', '"', '%', '\x14', '\x0E', '.', '?', '=', ':', '9']
In [2]: for i,c in enumerate(secret):
   ...:   secret[i] = chr(int(ord(c) ^ 65+(i*2)))

In [3]: print(''.join(secret)) 
Out[3]: Bagel_Cannon 

Bagel_Cannon proves another stage completed.

Stage3Form

Getting a bit more familiar with the code design of this application, Stage3Form is yet again, similar in design to the previous forms. Jumping directly to the boolean method isValidWeaponCode, we see another return statement with several inline method calls. We see another similar hard-coded byte array. Since it's being used with the SequenceEqual method, we can feel confident this likely represents our stage 3 secret. Parsing the return statement we see a call to BattleCatManagerInstance.InitializeBattleCat(). Before jumping over to this definition, two parameters are passed to this method, this.getCatGenetics() and the text provided in the text box by the user. getCatGenetics() method is defined in a single return statement that Base64 encodes this.PriorWeaponCode. Fortunately the variable is self-describing but we can confirm this assumption by finding stage3Form.PriorWeaponCode = stage2Form.WeaponCode; prior to the call to Stage3Form in the Main function. We now know the previous secret Bagel_Cannon is first Base64 encoded and the user text are passed to BattleCatManagerInstance.InitializeBattleCat(). Let's check out this Class.

BattleCatManagerInstance

This Class has four methods that performs various operations on the parameters passed to the meothds and we find subsequent calls to the remaining methods. Since this Class is self-contained, I chose to take a shortcut and let the code do the work. I copied the four methods into a new C# project. As shown in my Main function below, I called the InitializeBattleCat() method with the Base64 encoded Stage2 secret and the hard-coded secret found in Stage3. Depending on your environment, if you don't have a C# environment configured, online interpreters are available [3]. A more thorough review of the code would be required to translate the C# code into Python or some other language to execute a similar approach.

namespace BHFlareOn
{
	public static class BattleCatManagerInstance
	{
	    public static byte[] InitializeBattleCat(byte[] cat, byte[] data)
	    {
	      return BattleCatManagerInstance.AssignFelineDesignation(cat, (IEnumerable<byte>) data).ToArray<byte>();
	    }
	
	    private static byte[] InvertCosmicConstants(byte[] cat)
	    {
	      byte[] array = Enumerable.Range(0, 256).Select<int, byte>((Func<int, byte>) (i => (byte) i)).ToArray<byte>();
	      int i1 = 0;
	      int j = 0;
	      for (; i1 < 256; ++i1)
	      {
	        j = j + (int) cat[i1 % cat.Length] + (int) array[i1] & (int) byte.MaxValue;
	        BattleCatManagerInstance.CatFact(array, i1, j);
	      }
	      return array;
	    }
	
	    private static IEnumerable<byte> AssignFelineDesignation(byte[] cat, IEnumerable<byte> data)
	    {
	      byte[] s = BattleCatManagerInstance.InvertCosmicConstants(cat);
	      int i = 0;
	      int j = 0;
	      return data.Select<byte, byte>((Func<byte, byte>) (b =>
	      {
	        i = i + 1 & (int) byte.MaxValue;
	        j = j + (int) s[i] & (int) byte.MaxValue;
	        BattleCatManagerInstance.CatFact(s, i, j);
	        return (byte) ((uint) b ^ (uint) s[(int) s[i] + (int) s[j] & (int) byte.MaxValue]);
	      }));
	    }
	
	    private static void CatFact(byte[] s, int i, int j)
	    {
	      byte num = s[i];
	      s[i] = s[j];
	      s[j] = num;
	    }
	}
    
	public static class Program
	{
	  public static void Main(string[] args)
	  {
	    byte[] test = BattleCatManagerInstance.InitializeBattleCat(Encoding.UTF8.GetBytes(Convert.ToBase64String(Encoding.UTF8.GetBytes("Bagel_Cannon"))), new byte[21]
	    {
		    (byte) 95,
		    (byte) 193,
		    (byte) 50,
		    (byte) 12,
		    (byte) 127,
		    (byte) 228,
		    (byte) 98,
		    (byte) 6,
		    (byte) 215,
		    (byte) 46,
		    (byte) 200,
		    (byte) 106,
		    (byte) 251,
		    (byte) 121,
		    (byte) 186,
		    (byte) 119,
		    (byte) 109,
		    (byte) 73,
		    (byte) 35,
		    (byte) 14,
		    (byte) 20
	    });
	    Console.WriteLine(Encoding.UTF8.GetString(test));
	  }
	}
}

Execution yields, Defeat_them_with_love.

Conclusion

Perhaps one of Flare-On's easier challenges, but I suspect we're in for a much more challenging time when the challenge kicks off August 17 [4]. We'll be adding more blog posts of write-ups for that upcoming challenge. Check back for more. Feedback welcome!


  1. https://twitter.com/Invalid_handle/status/1159192250816974849 ↩︎

  2. https://www.jetbrains.com/decompiler/ ↩︎

  3. https://rextester.com ↩︎

  4. http://www.flare-on.com ↩︎