How I beat Crypto Challenge 3

For the 2013 GRRCon Crypto Challenge 3 we were given an image that needed to be decrypted, there were multiple levels and the final code could be used to get a free ticket to the conference.


Crypto Challenge 3

Crypto Challenge 3

The first level was probably the toughest part. While looking at it during lunch, a friend of mine and I noticed that it was braille. We went through and converted the first few letters and I noticed that the words IBMIsTheDevil were in there. The challenge with this part was that if you make a single mistake in the translation you would not know exactly where that mistake was without going through the entire image over again. I first tried to decode it by hand, that became annoying quick, so I looked to see if there was some OCR software out there for reading braille. Luckily for me I have a good friend of mine that has a PHD in image processing, unfortunately for me he told me he doesn’t know of any software that could OCR braille.

Take 2


braille-big
After failure to find a good OCR software I decided to write a rudimentary OCR software myself. Things to notice, #1 The image can be split up into blocks of equal size. #2 The only value that matters was to get what was at the center of the circle. The image to the right shows a blown up version of a single letter. To perform OCR I simply created a C# application that would sample the center pixle of each dot and create an array of Boolean values, if the pixle was black it value was true, if it was white value was false.� Note: I did have to convert the gif to a PNG.

After creating the array simply check them against the known braille alphabet.

WARNING: The code I’m showing was just for this one task and was built in approximately 2 hours without any testing, it is not meant in any way shape or form to be used in a production environment or for any other task.

BrailleReader.cs
You’ll need to add a reference to system.drawing.


   class BrailleReader
    {
        private Bitmap image;
        int col, row;
        Point lastPoint;

        bool capital = false;
        bool number = false;

        Color Black = new Color();

        private Dictionary<int[], string> translate;

        public BrailleReader()
        {
            col = 0;
            row = 0;
            this.Black = Color.FromArgb(4, 2, 4);

            // setup dictionary
            translate = new Dictionary<int[], string>();
            translate.Add(new int[] { 0 }, "a");
            translate.Add(new int[] { 0, 1 }, "b");
            translate.Add(new int[] { 0, 3 }, "c");
            translate.Add(new int[] { 0, 3, 4 }, "d");
            translate.Add(new int[] { 0, 4 }, "e");
            translate.Add(new int[] { 0, 1, 3 }, "f");
            translate.Add(new int[] { 0, 1, 3, 4 }, "g");
            translate.Add(new int[] { 0, 1, 4 }, "h");
            translate.Add(new int[] { 1, 3 }, "i");
            translate.Add(new int[] { 1, 3, 4 }, "j");
            translate.Add(new int[] { 0, 2 }, "k");
            translate.Add(new int[] { 0, 1, 2 }, "l");
            translate.Add(new int[] { 0, 2, 3 }, "m");
            translate.Add(new int[] { 0, 2, 3, 4 }, "n");
            translate.Add(new int[] { 0, 2, 4 }, "o");
            translate.Add(new int[] { 0, 1, 2, 3 }, "p");
            translate.Add(new int[] { 0, 1, 2, 3, 4 }, "q");
            translate.Add(new int[] { 0, 1, 2, 4 }, "r");
            translate.Add(new int[] { 1, 2, 3 }, "s");
            translate.Add(new int[] { 1, 2, 3, 4 }, "t");
            translate.Add(new int[] { 0, 2, 5 }, "u");
            translate.Add(new int[] { 0, 1, 2, 5 }, "v");
            translate.Add(new int[] { 1, 3, 4, 5 }, "w");
            translate.Add(new int[] { 0, 2, 3, 5 }, "x");
            translate.Add(new int[] { 0, 2, 3, 4, 5 }, "y");
            translate.Add(new int[] { 0, 2, 4, 5 }, "z");

        }

        public void Process(string path, int columns)
        {
            if (!File.Exists(path))
            {
                return;
            }

            using (image = new Bitmap(path))
            {
                for (int i = 0; i < 28; i++)
                {
                    col = 0;
                    this.lastPoint = new Point(0, i * 50);

                    while (col < columns)
                    {
                        var result = GetNextLetter();
                        Console.Write(result);
                        col++;
                    }
                }
            }
        }

        private string GetNextLetter()
        {
            bool[] brailleCode = new bool[6] { false, false, false, false, false, false };

            Rectangle section = new Rectangle(FindNext(), new Size(17, 27));

            Bitmap bmp = new Bitmap(section.Width, section.Height);
            Graphics g = Graphics.FromImage(bmp);

            g.DrawImage(this.image, 0, 0, section, GraphicsUnit.Pixel);

            brailleCode[0] = bmp.GetPixel(3, 3) == this.Black;
            brailleCode[1] = bmp.GetPixel(3, 13) == this.Black;
            brailleCode[2] = bmp.GetPixel(3, 23) == this.Black;
            brailleCode[3] = bmp.GetPixel(13, 3) == this.Black;
            brailleCode[4] = bmp.GetPixel(13, 13) == this.Black;
            brailleCode[5] = bmp.GetPixel(13, 23) == this.Black;

            string result = FindString(brailleCode);

            return result;
        }

        private string FindString(bool[] brailleCode)
        {
            string result = string.Empty;

            foreach (var item in this.translate)
            {
                result += GetString(brailleCode, item.Key, item.Value);
            }

            if (GetString(brailleCode, new int[] { 2, 3, 4, 5 }, "NUMBER") != string.Empty)
            {
                number = true;
                return string.Empty;
            }
            else if (GetString(brailleCode, new int[] { 4, 5 }, "STOP") != string.Empty)
            {
                number = false;
                return string.Empty;
            }

            if (GetString(brailleCode, new int[] { 5 }, "UPPER") != string.Empty)
            {
                this.capital = true;
                return string.Empty;
            }

            if (number && result != string.Empty)
            {
                result = ConvertToNumber(result);
            }

            if (capital)
            {
                result = result.ToUpper();
                capital = false;
            }

            return result;
        }

        private string ConvertToNumber(string value)
        {
            value = value.ToLower();
            switch (value)
            {
                case "j":
                    value = "0";
                    break;
                case "a":
                    value = "1";
                    break;
                case "b":
                    value = "2";
                    break;
                case "c":
                    value = "3";
                    break;
                case "d":
                    value = "4";
                    break;
                case "e":
                    value = "5";
                    break;
                case "f":
                    value = "6";
                    break;
                case "g":
                    value = "7";
                    break;
                case "h":
                    value = "8";
                    break;
                case "i":
                    value = "9";
                    break;
                default:
                    return string.Empty;
            }

            return value;
        }

        private Point FindNext()
        {
            int x = lastPoint.X + 7;
            int y = lastPoint.Y + 7;

            lastPoint.X = x + 23;
            return new Point(x, y);
        }

        public string GetString(bool[] brailleCode, int[] indexes, string letter)
        {
            for (int i = 0; i < brailleCode.Length; i++)
            {
                if ((brailleCode[i] && !indexes.Contains(i)) || (indexes.Contains(i) && !brailleCode[i]))
                {
                    return string.Empty;
                }
            }

            return letter;
        }
    }

Program.cs

    class Program
    {
        static void Main(string[] args)
        {
            BrailleReader reader = new BrailleReader();
            reader.Process(args[0], 17);
        }
    }


crypto3-screenshot

Success!
IBMisthedevild51feacaa3d01e8a70d10b580c1cb9b0557899dbc086e5182c1eb8ed9845823aee907f7ce41448a7649355c087d705fc6ff9fcb97582e0d74735d8cdd73810db961d7ed547d04850c39e7e196a1e88e3d43e55cb4712bf073a10aab690ce98e0560237963558f9e0e2f6643aa585317b487a4258606adb24d5208ce8d689d73a3d952f6cf34c2dcec55d96b035a3c687ee0ad0357e5c0586

Stage 2 – Break the Cipher

The IBM is the devil part lead me to DES. If you study the history of cryptography you know is related to the Lucifer algorithm which later became DES. Just as a little background, DES is essentially the same algorithm as Lucifer except the S-Boxes (Substitution Boxes) were given to IBM by the NSA as well as a reduction of the key size from 128 bits to 56 bits. DES was the gold standard for encryption algorithms through even the 1990′s because of it’s resistance to cryptanalysis techniques that were being discovered. So using an online tool to decrypt DES I began guessing passwords, Lucifer was one of the first guesses I took and it partially worked.


crypto3-screenshot2

You’ll notice that the readable characters are 8 characters, a char is typically 8 bits in ASCII which in this case would mean 8 Characters * 8 Bits = 64 bits. 64 bits is exactly 1 block for DES. Typically when you’re encrypting you’d want to use CBC mode (or better), which stands for cipher block chaining. CBC means that once a block is encrypted, that result is XORed with the next block to give you an avalanche effect over your entire cipher text rather than just over a single block. Because only the first block of cipher text was decrypted that lead me to believe that the mode was not CBC but ECB (Electronic Code Book). In ECB mode each block is encrypted separately, it’s not recommended to ever use this mode in a production setting.

Switching to ECB Mode the following was the result.


crypto3-screenshot3

Stage 3 – All Out War

It seems that the writer of this challenge really loved crypto history. Archduke Franz Ferdinand was a royal from Austria who’s assassination set off World War I, again if you’re a student of cryptography you know that World War I was huge for cryptography. So now the challenge was to #1 figure out which algorithm was being used, and #2 figure out the key. At the time the state of the art were transposition ciphers, these were used by both the Allies as well as the Axis powers. I worked through a few ciphers but I was hell bent on ADFGVX, which I couldn’t get to work no matter how much I tried. Then I realized it was a little known cipher called Übchi, which was used by the Germans around that time. Now for that key… I noticed that there was a game on the GRRCon website (this was my first year going so I didn’t have any knowledge of the game before hand), game was called Hacker Feud… so I tried it and….

JayZis1337:u537h15pr0m0c0d370cl41my0ur71ck37:1fy0ur3h4v1n6l0ckp1ck1n6pr0bl3m51f33lb4df0ry0u50n160799l0ckp1ck5bu71ju57n33d1

Stage 4 – Free Ticket

Simply copy and paste the code 1fy0ur3h4v1n6l0ckp1ck1n6pr0bl3m51f33lb4df0ry0u50n160799l0ckp1ck5bu71ju57n33d1 into eventbright and I got myself 1 free ticket! So… if you’re having lock picking problems I feel bad for you son, I got 99 lock picks but I just need 1!