Decrypting Control4 Lua

Extraction and unpacking of game archives and compression, encryption, obfuscation, decoding of unknown files
HighlightRepulsive98
Posts: 2
Joined: Sun Aug 22, 2021 9:46 pm

Decrypting Control4 Lua

Post by HighlightRepulsive98 »

Hey can someone help me decrypt some Control4 clipsal c-bus automation driver files?
I have some c-bus automation equipment at home that id like to integrate with a control4 system. I figure if i can see how the current drivers are written i can build from these to create additiona drivers for other commuication of functions such as temperature and energy consumption which currently are not supported.

I realise this is a bit taboo, however the drivers are free and over 10 years old.

The drivers in question are located here: https://drivers.control4.com/solr/drive ... Clipsal%22

Specifically the ethernet driver and a dimmer / light would be great to revel how they work, all of them would be evern better :P. I have tried to follow this https://pushstack.wordpress.com/2016/03 ... ecryption/ but have not had much luck so far.
atom0s
Posts: 250
Joined: Sat Dec 27, 2014 8:49 pm

Re: Decrypting Control4 Lua

Post by atom0s »

Here's their own code used to encrypt the scripts from their driver editor app:

Code: Select all

using System;
using System.Security.Cryptography;
using System.Text;

namespace ScriptEncryptor
{
   // Token: 0x02000009 RID: 9
   internal class C4AES
   {
      // Token: 0x06000044 RID: 68 RVA: 0x000060A4 File Offset: 0x000042A4
      internal static string encrypt(string plaintext)
      {
         string text = Convert.ToBase64String(C4AES.xorWithEncryptedStream(plaintext));
         StringBuilder stringBuilder = new StringBuilder();
         for (int i = 0; i < text.Length; i += 0x3B)
         {
            stringBuilder.AppendLine(text.Substring(i, Math.Min(0x3B, text.Length - i)));
         }
         return stringBuilder.ToString();
      }

      // Token: 0x06000045 RID: 69 RVA: 0x000060F8 File Offset: 0x000042F8
      private static byte[] xorWithEncryptedStream(string plaintext)
      {
         ICryptoTransform cryptoTransform = new RijndaelManaged
         {
            BlockSize = 0x80,
            KeySize = C4AES.AES_KEY.Length * 8,
            Key = C4AES.AES_KEY,
            Mode = CipherMode.ECB,
            IV = new byte[0x10]
         }.CreateEncryptor();
         byte[] bytes = Encoding.ASCII.GetBytes(plaintext);
         byte[] array = new byte[bytes.Length];
         byte[] inputBuffer = new byte[0x10];
         byte[] array2 = new byte[0x10];
         for (int i = 0; i < bytes.Length; i += 0x10)
         {
            C4AES.incrementCounter(ref inputBuffer);
            cryptoTransform.TransformBlock(inputBuffer, 0, 0x10, array2, 0);
            for (int j = 0; j < Math.Min(0x10, bytes.Length - i); j++)
            {
               array[i + j] = (bytes[i + j] ^ array2[j]);
            }
         }
         return array;
      }

      // Token: 0x06000046 RID: 70 RVA: 0x000061D0 File Offset: 0x000043D0
      private static void incrementCounter(ref byte[] counter)
      {
         for (int i = 0xF; i >= 0; i--)
         {
            byte[] array = counter;
            int num = i;
            array[num] += 1;
            if (counter[i] != 0)
            {
               return;
            }
         }
      }

      // Token: 0x0400004F RID: 79
      private const int AES_BLOCK_SIZE = 0x10;

      // Token: 0x04000050 RID: 80
      private static readonly byte[] AES_KEY = new byte[]
      {
         0x7D,
         0xB2,
         8,
         0xC0,
         0x69,
         0x9B,
         0x14,
         0xB4,
         0x6E,
         0xF8,
         0x1B,
         0x97,
         0xF3,
         0x9A,
         0xFC,
         0x98,
         0x68,
         0xDD,
         0x15,
         0x8F,
         0x16,
         0x36,
         0xF7,
         0xF4,
         0x38,
         0x66,
         0xDF,
         0xE5,
         0x5F,
         0x19,
         0xF4,
         0x98
      };
   }
}



Should be everything you need to understand write your own reversal of this to do the decryption.
HighlightRepulsive98
Posts: 2
Joined: Sun Aug 22, 2021 9:46 pm

Re: Decrypting Control4 Lua

Post by HighlightRepulsive98 »

Thanks, Would you mind going over the process with me? Can i reach you on Discord?
atom0s
Posts: 250
Joined: Sat Dec 27, 2014 8:49 pm

Re: Decrypting Control4 Lua

Post by atom0s »

The process is pretty straight forward due to how they are using AES. It's not the most ideal setup and is more or less just used to deter script kiddy people from copy/pasting stuff. I don't really think this was intended for actual security, but if it was it was pretty poorly planned out.

Based on the code I pasted above, there's a few steps taken when reading the script back from its encrypted state. In reverse, those steps would be:

- Read the full script block within the <script encrypted="1">...</script> tags.
- Remove all linebreaks from that text as the encryptor breaks it into 59 character lines.
- Base64 decode the string to remove the last layer of encoding.

This will remove the first layer of junk. The next part is where AES is being used. But it's not being used in a 'common' or 'standard' manner. Instead, they are using it to encrypt a 'rolling' key they call a counter. This part loops over the entire block of text we just base64 decoded, and increments this counter then does an xor pass over the bytes at the given position in the main decoded data.

So step wise this does:

- Increment a counter buffer.
- Encrypt the counter buffer with AES against a known static key.
- Xor decrypt the current decoded data against the encrypted counter buffer data. (16 bytes are done at each iteration.)
- Repeat until you have done this against the whole decoded buffer.

So the actual Lua script itself is just xor'd and then base64'd pretty much. The AES stuff is only being done to the xor key counter, which isn't really that 'secure' with how this is setup.

Here's a C# app I wrote (requires .NET 5.0) which will decrypt those driver files scripts if they contain an encrypted script. Just drag and drop and it will spit out a .lua file in the same place the original driver file was at.


Here is the code of the app too:

Code: Select all

/**
 * c4decryptor - Copyright (c) 2021 atom0s
 *
 * Contact: https://atom0s.com/
 * Contact: https://discord.gg/UmXNvjq
 */

namespace c4decryptor
{
    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
    using System.Text.RegularExpressions;

    internal class Program
    {
        /// <summary>
        /// Regex pattern to match the start of an encrypted script tag.
        /// </summary>
        private const string SCRIPT_START_TAG = "<script encryption=\"1\">";

        /// <summary>
        /// Regex pattern to match the end of an encrypted script tag.
        /// </summary>
        private const string SCRIPT_STOP_TAG = "</script>";

        /// <summary>
        /// The AES key used for the decryption of the script.
        /// </summary>
        private static readonly byte[] AES_KEY = new byte[] { 0x7D, 0xB2, 0x08, 0xC0, 0x69, 0x9B, 0x14, 0xB4, 0x6E, 0xF8, 0x1B, 0x97, 0xF3, 0x9A, 0xFC, 0x98, 0x68, 0xDD, 0x15, 0x8F, 0x16, 0x36, 0xF7, 0xF4, 0x38, 0x66, 0xDF, 0xE5, 0x5F, 0x19, 0xF4, 0x98 };

        /// <summary>
        /// Application entry point.
        /// </summary>
        /// <param name="args"></param>
        private static void Main(string[] args)
        {
            // Handle no arguments..
            if (args.Length == 0 || !File.Exists(args[0]))
            {
                Console.WriteLine("----------------------------------------------------------");
                Console.WriteLine("* c4decryptor - Copyright (c) 2021 atom0s");
                Console.WriteLine("* ");
                Console.WriteLine("* Contact: https://atom0s.com/");
                Console.WriteLine("* Contact: https://discord.gg/UmXNvjq");
                Console.WriteLine("----------------------------------------------------------");
                Console.WriteLine("");
                Console.WriteLine("Usage:");
                Console.WriteLine("atom0s!c4decryptor.exe [driver_file]");
                Console.WriteLine("");
                Console.WriteLine("Drag and drop a file onto this program for ease of use.");
                return;
            }

            // Read the given files text..
            var text = File.ReadAllText(args[0]);

            // Find the regex pattern to the encrypted script, if one exists..
            var regex = Regex.Matches(text, SCRIPT_START_TAG + "(.*)" + SCRIPT_STOP_TAG, RegexOptions.IgnoreCase | RegexOptions.Singleline);
            if (regex == null || regex.Count == 0 || regex[0].Groups.Count < 2)
            {
                Console.WriteLine("Invalid file; no encrypted script could be found.");
                return;
            }

            // Clean the script into a single line of text..
            var script = regex[0].Groups[1].Value.Replace("\r\n", "");

            // Remove the Base64 encoding..
            var data = Convert.FromBase64String(script);

            // Decrypt the script data via AES and xoring..
            var encryptor = new RijndaelManaged
            {
                BlockSize = 0x80,
                KeySize = AES_KEY.Length * 8,
                Key = AES_KEY,
                Mode = CipherMode.ECB,
                IV = new byte[0x10]
            }.CreateEncryptor();

            var counter = new byte[0x10];
            var buffer = new byte[0x10];
            for (var x = 0; x < data.Length; x += 16)
            {
                Program.CounterIncrement(ref counter);
                encryptor.TransformBlock(counter, 0, counter.Length, buffer, 0);

                for (var y = 0; y < Math.Min(0x10, data.Length - x); y++)
                {
                    data[x + y] = (byte)(data[x + y] ^ buffer[y]);
                }
            }

            var decrypted = Encoding.ASCII.GetString(data);

            // Output the decrypted script to a file named from the original..
            var file = args[0] + ".lua";
            File.WriteAllText(file, decrypted);

            Console.WriteLine("Decrypted! File was saved to:");
            Console.WriteLine(file);
        }

        /// <summary>
        /// The encryption counter data used as the xor key.
        /// </summary>
        /// <param name="counter"></param>
        private static void CounterIncrement(ref byte[] counter)
        {
            for (var x = 0x0F; x >= 0; x--)
            {
                counter[x] += 1;
                if (counter[x] != 0)
                    return;
            }
        }
    }
}
danielhitch
Posts: 1
Joined: Mon Sep 19, 2022 11:10 am

Re: Decrypting Control4 Lua

Post by danielhitch »

Hi,

Sorry to drag up an old thread.

Did you have any success using this script?

I am trying to decrypt this driver but, the script is saying

Code: Select all

Invalid file; no encrypted script could be found.

I have extracted the driver using 7-Zip and the driver within is named driver.lua.encrypted but doesn't seem to follow the convention in the RegEx of the attached C# solution..