Mechanical brute force

//

This article was published on Security By Default.


Jhonny5

Last month, our friend Lorenzo Martínez showed us how to always beat Mezcladitos intercepting communications between the game and the server.



Well, maybe is cheating, but at least we're not hacking the system ;)

This is just a harmless demonstration of mechanical brute force. When we think about brute force, often comes to mind the "trial and error" software to encrypt random passwords and check if they are valid. However we can apply brute force mechanically to all kinds of things: virtual keyboards, physical keyboards, dials, etc

In this demo I played Facebook version of Mezcladitos, but if I had had some handy servos and an arduino I would have been playing directly on the iPhone screen (with a simple mechanical "typer").

The first thign to do was to create an algorithm that solves the grid, brute forcing all possible words using a dictionary containing all words existing words in Spanish language.

Since we only have two minutes to play, we need to calculate the words in the shortest time possible, so in addition to optimizing the algorithm, I launched it as 16 threads (one per grid box) to take advantage of all CPU cores. This algorithm runs through all possible paths from a given box and check if the resulting word exists in the dictionary.


using MezcladitosPWNer;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading;
using System.Linq;

namespace MezcladitosPWNer {

    public class FuerzaBruta {
        private ManualResetEvent _doneEvent;
        private string _letra;
        private int _x;
        private int _y;
        private List<string> _ruta;
        private string _palabra;

        private int[,] adyacentesA = { { -1, -1 }, { 0, -1 }, { 1, -1 }, { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 } };
        private int[,] adyacentesB = { { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 } };
        private int[,] adyacentesC = { { 1, 0 }, { 1, 1 }, { 0, 1 } };
        private int[,] adyacentesD = { { 0, 1 }, { -1, 1 }, { -1, 0 } };
        private int[,] adyacentesE = { { -1, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 }, { 1, 0 } };
        private int[,] adyacentesF = { { 0, -1 }, { 1, -1 }, { 1, 0 } };
        private int[,] adyacentesG = { { -1, 0 }, { -1, -1 }, { 0, -1 } };
        private int[,] adyacentesH = { { 0, -1 }, { 1, -1 }, { 1, 0 }, { 1, 1 }, { 0, 1 } };
        private int[,] adyacentesI = { { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 }, { 0, -1 } };



        public FuerzaBruta(int x, int y, List<string> ruta, string palabra, ManualResetEvent doneEvent) {
            _doneEvent = doneEvent;
            _x = x;
            _y = y;
            _ruta = ruta;
            _palabra = palabra;
        }


        public void ThreadPoolCallback(Object threadContext) {
            int threadIndex = (int)threadContext;
            ady(_x, _y, _ruta, _palabra);
            _doneEvent.Set();
        }


        private void ady(int x, int y, List<string> pRuta, string palabra) {
            if (_doneEvent.WaitOne(0)) {
                return;
            }

            List<string> ruta;
            ruta = new List<string>(pRuta);
            Regex regPart;
            int palMaxTam;
            Match m;

            if (ruta.Capacity > Globales.maxLength - 1) {
                return;
            }

            ruta.Add(x.ToString() + y.ToString());
            palabra += Globales.cas[x][y];

            if (palabra.Length == 1) {
                _letra = palabra;
            }

            if (palabra.Length >= Globales.minLength) {
                palMaxTam = Globales.maxLength - palabra.Length;

                if (Globales.arrays[_letra].Contains(palabra)) {
                    Globales.frm.pwnWord(palabra, new List<string>(ruta));
                    Globales.frm.AsyncWriteLine(palabra + " ");
                }

                regPart = new Regex(@"#(" + palabra + "\\S{" + palMaxTam + "})#");
                m = regPart.Match(Globales.cadenas[_letra]);

                if (!m.Success) {
                    return;
                }
            }

            if (y > 0 && y < 4 - 1 && x > 0 && x < 4 - 1) {
                for (int i = 0; i < adyacentesA.GetLength(0); i++) {
                    profundizar(x + adyacentesA[i, 0], y + adyacentesA[i, 1], ruta, palabra);
                }
            }
            if (y == 0) {
                if (x > 0 && x < 4 - 1) {
                    for (int i = 0; i < adyacentesB.GetLength(0); i++) {
                        profundizar(x + adyacentesB[i, 0], y + adyacentesB[i, 1], ruta, palabra);
                    }

                } else if (x == 0) {
                    for (int i = 0; i < adyacentesC.GetLength(0); i++) {
                        profundizar(x + adyacentesC[i, 0], y + adyacentesC[i, 1], ruta, palabra);
                    }
                } else if (x == 4 - 1) {
                    for (int i = 0; i < adyacentesD.GetLength(0); i++) {
                        profundizar(x + adyacentesD[i, 0], y + adyacentesD[i, 1], ruta, palabra);
                    }
                }
            }
            if (y == 4 - 1) {
                if (x > 0 && x < 4 - 1) {
                    for (int i = 0; i < adyacentesE.GetLength(0); i++) {
                        profundizar(x + adyacentesE[i, 0], y + adyacentesE[i, 1], ruta, palabra);
                    }
                } else if (x == 0) {
                    for (int i = 0; i < adyacentesF.GetLength(0); i++) {
                        profundizar(x + adyacentesF[i, 0], y + adyacentesF[i, 1], ruta, palabra);
                    }
                } else if (x == 4 - 1) {
                    for (int i = 0; i < adyacentesG.GetLength(0); i++) {
                        profundizar(x + adyacentesG[i, 0], y + adyacentesG[i, 1], ruta, palabra);
                    }
                }
            }
            if (x == 0 && y != 0 && y != 4 - 1) {
                for (int i = 0; i < adyacentesH.GetLength(0); i++) {
                    profundizar(x + adyacentesH[i, 0], y + adyacentesH[i, 1], ruta, palabra);
                }
            }
            if (x == 4 - 1 && y != 0 && y != 4 - 1) {
                for (int i = 0; i < adyacentesI.GetLength(0); i++) {
                    profundizar(x + adyacentesI[i, 0], y + adyacentesI[i, 1], ruta, palabra);
                }
            }
        }

        private void profundizar(int x, int y, List<string> ruta, String palabra) {
            if (ruta.LastIndexOf(x.ToString() + y.ToString()) == -1) {
                ady(x, y, ruta, palabra);
            }
        }
    }
}

Once I have all the results, I hook mouse with SetWindowsHookEx, so I can control the mouse programatically and click boxes automatically for each word.

As you can see in the video, before starting the game is needed a calibration, by clicking on the first two boxes and "Return", to calculate the coordinates of all the squares on the board.


public static IntPtr mouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
         if (nCode >= 0 && InputHooks.MouseMessages.WM_LBUTTONDOWN == (InputHooks.MouseMessages)wParam && Globales.ctrlDOWN == true) {
             InputHooks.MSLLHOOKSTRUCT hookStruct = (InputHooks.MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(InputHooks.MSLLHOOKSTRUCT));
            if (Globales.siguiente == 0) {
                 Globales.coordCasilla1 = hookStruct.pt;
             } else if (Globales.siguiente == 1) {
                 Globales.coordCasilla2 = hookStruct.pt;
             } else if (Globales.siguiente == 2) {
                 Globales.coordBtnIntroducir = hookStruct.pt;
                 Globales.startPWN = true;
             }
             Globales.siguiente++;
         }
             return InputHooks.CallNextHookEx(mouseHookID, nCode, wParam, lParam);
     }
while (Globales.palabrasEncontradas.Count > 0) {
             palabra = Globales.palabrasEncontradas.Pop();
             ruta = Globales.rutasEncontradas.Pop();

             for (int i = 0; i < ruta.Count; i++) {
                 InputHooks.ClickLeftMouseButton(Globales.coordCasilla1.X + (Globales.calibrado * Convert.ToInt32(ruta[i][1].ToString())),
                                            Globales.coordCasilla1.Y + (Globales.calibrado * Convert.ToInt32(ruta[i][0].ToString())));
                 System.Threading.Thread.Sleep(50);
             }
             System.Threading.Thread.Sleep(50);
             InputHooks.ClickLeftMouseButton(Globales.coordBtnIntroducir.X, Globales.coordBtnIntroducir.Y);
             System.Threading.Thread.Sleep(50);
         }

We have "brute forced" to win a game, but this kind of technique could be used for less playful things like to gain unauthorized access to websites or physical places, where extra security measures are not in place.

In this case we are used the mouse as an automated input method. Some websites (even some banks) use virtual keyboards for login using the mouse. Sometimes even randomly changing the order of the letters or numbers (nothing that can not be skipped with an OCR algorithm). It seems that this scrambling method adds an extra layer of security, but if they don't limit the number of attempts, it can be easily brute forced (and if you are looking a 4 digit PIN, it will not take more than a few minutes).

As I said before, having a arduino and a few engines, ** we can brute force practically anything very easily and cheaply ** as long as we do not limit the number of attempts, which makes us wonder how secure are devices that we use on a daily basis, and how often they lack certain safeguards because attacks "seem" unlikely. Or how many times we ourselves override these safeguards just for convenience.

In the digital era, there are many people (especially older people, or with little computer knowledge) who think analog is safer than digita; so here are a few examples to beat that urban myth (Note: Some of these examples are locked after a number of attempts, but I add them just as an example of things that can be physically attacked):


01
02
03
04
05
06
07
08
09

Here is the full source code (wordlist not included). https://github.com/moebiuz/mezclaPWNr