Thread Rating:
  • 1 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5

[Project] Cracking Hashes with Arduino

#1
Cracking Hashes With Arduino
Written by Lain; Released under GPLv2 License; Published for Makestation

Table of Contents:
0x00 - Introduction/Materials/Disclaimer
0x01 - Setting up IDEs/Toolchains/Drivers
0x02 - Setting up your Arduino Board
0x03 - Basic Hashing Logic
0x04 - Hashing a Serial Command
0x05 - Communicating with Python
0x06 - Automating the Process
0x07 Making it Fast
0x08 - Conclusion


0x00 - Introduction:

Welcome to the guide. This project has been in the works for a month or two, just working on-and-off. That doesn't mean it's very complex, it's actually rather simple once boiled down, and I've added lots of comments to make it super-easy to understand. That being said, I will assume at least some degree of programming competency (I'm not explaining what a for-loop does. Figure it out yourself.) I'm also assuming a very basic degree of competency with Python and Arduino language in particular, but since they're still very basic, that shouldn't be much of an issue as long as you still have some degree of knowledge generally.

The complexity of this project arises from two points: understanding communication between devices and understanding timing. You'll see what I mean as I dive deeper into the later-stages of the project. These concepts are explained to the best of my ability.

If you're unfamiliar with the concept of a hash, then I'll give you a brief explanation:

When you login to a website (like Instructables) with your password, the password stored on the website generally isn't the password you send (under good security conditions.) Instead, the server will store something completely different, so in the case that it gets hacked, your password is still unknown to the attacker. That different 'password' is what you call a hash, and it's NOT actually encrypted. With encryption, you can decrypt the message, but in the case of hashing, you cannot generate the original string from the generated. Thus, to break or 'crack' a hash, we simply try all sorts of permutations until we find one that works and generates the same hash value, and we consider this to be the 'original.'

If you're involved in the security scene, or keep up-to-date with all sorts of data breaches, then you're probably aware of this fact, and you're probably also aware that this type of attack is called 'brute-forcing.' If we had an unlimited amount of time and power, we could theoretically brute-force (crack) any hash possible. Unfortunately, we don't have unlimited time, but with enough power on our computers, we could still crack many hashes in a relatively short amount of time. In modern days, we use graphics processors (graphics cards, or GPUs) to crack hashes since they can handle many computations at once.

The trade-off for the massive amounts of power in your GPU or computer is that we use lots of power, literally! The voltage to keep these setups running will make your power bills spike through the roof, and we could even call it unsustainable.


What you'll need:
  • An Arduino board (I'm using a MEGA2560 clone from China)
  • A cable to plug it into your computer
  • Your computer
  • The Arduino IDE 
  • The Python3 Toolchain
Optionally, you may also want to install the PyCharm IDE which is what I use for the Python segment which I use in sections 5 through 7.

DISCLAIMER: This project was initially uploaded to Instructables as an entry in a contest. The Instructables team have since unpublished the project as a violation of their ToS (no password attacks or hacking-related content.) I don't blame them for it and hold no hard feelings, and will instead come up with a different project to publish, maybe along the lines of blockchains and IoT.

I actually do agree with their ruling, since this project can potentially be used for malicious purposes, and as part of the disclaimer, I hold no liability for damages incurred as a result of this project and guide.

By extension, due to the GPLv2 license used in this guide (and respective code samples,) I do not provide any warranty and support. Everything is provided AS-IS. That being said, I will update the guide as I see fit (if I notice typos and whatnot.)

Without further ado, let's start walking through some basic setup.


0x01 - Setting up IDEs, Toolchains, & Drivers:


It sounds like a lot, but I promise it really isn't. First, you'll need the Arduino IDE.
You can install it from the official site here, just choose the version you'd like.

Since I'm using Windows, I installed the Windows Installer package. I am an administrator on my own computer.
I chose the installer over the Windows Store App for two reasons:

  1. The Windows Store version is usually a version out-of-date. At the time of writing this, the Windows store version is on 1.8.9 and the installer is on 1.8.10.
  2. Since Store apps install to a different location and use different defaults, you may need to adjust/adapt to later steps when installing libraries.
In the options screen of the Arduino installer, this is what I have set:

[Image: BFQRQ3g.jpg]

To ensure that your board will work, select the Driver option. For easier development later on, you can select the .ino file association as well. Shortcuts are optional, since I don't have icons on my desktop, I don't need one there. Instead, I launch everything from Start menu.

Let the installer finish, and finally, allow the driver installation pop-ups you get. Your board won't work without them.

Congratulations! the Arduino IDE is installed. To ensure that it works, run it from one of the shortcuts or from your install path.

Installing Python

If you don't actively develop with Python, you'll need to install it now. but if you do develop with Python, first check that you have the correct version.
For the scripting in this guide, I use Python3 as it's the latest and greatest with long-term support. If you think you have Python installed, then run this in your command line:
Code:
python --version
If you get anything that isn't a 3.X.X version, then check to see if you might have it installed side-by-side with python2:
Code:
python3 --version

If you get an error saying command not found, you need to install it. If neither of them return a version number that starts with 3, you need to install 3 (or port the script part over for your respective version.)

Go to the official Python website and hover over Downloads. You'll see the 3.8.0 button (at the time of writing this.) Click that button; it's a direct download so give it a second to start. If it downloads the wrong type of file for your platform (might be different for MacOS or Linux,) click All Releases and download your respective version.

In the installer, make sure that you check the box saying you'll add it to the PATH variable. This is critical for running Python from the command-line. Select the rest of the default values (or the Typical/full install with pip) and let it run for a minute or so until it finishes installation. All set!

Optional: Installing PyCharm

As stated, this is optional. I use PyCharm as my Python editor/IDE since it's just a little easier to use when everything is all in one place. You don't need it, and if you're familiar with a different text editor and manually running scripts, then you should just stick to that.

As before, head over to the official website. Select your OS and select the Community edition. We're not selling this project, so we don't need a commercial license that Pro gives us.

Once it downloads, run the installer. Once you get to the other options, just know that I have everything checked. If you don't understand what the option does, then you should probably leave it alone. But most modern computers are 64 bit, so you can add the 64 bit launchers if you'd like.

Let it install, and once it's done, you may be prompted for a reboot. I'll tell you now, you don't need it. If you want to reboot your PC, more power to you. But if your PC takes 5 minutes to start up, then you don't need to.

After all this is done, you should be ready to start setting up your Arduino board!


0x02 - Setting up your Arduino Board:

As a sidenote: I'm using the MEGA2560. It's a clone from China because they're much cheaper than the 40$+ on the official website. That being said, you don't need a MEGA2560. An UNO will do, or the tiny little Nano or Pro Micro. This Arduino code is relatively portable and works fine with other boards with minimal modification, if any at all.

The Arduino IDE will default to using the UNO since it's by far their most popular board. In my case, I need to do additional setup (and you should probably make sure everything here is set properly too, thus I'm not considering any of this optional.)

First, open the Arduino IDE you installed and you'll have yourself a nice blank sketch. At the top-bar menu, go to Tools > Board: "XXX" > Your board, as shown in the image below:
[Image: nEOHfMr.jpg]

Since I'm using the MEGA2560, I selected that one. Choose which one your board is (and make sure it's plugged in.)

Next, we need to set the COM port, or how the Arduino will communicate with the computer to get programmed. Since I'm on Windows, I'll show you how to do it on Windows and leave other OS's to Google.

Open the Start Menu, type in 'Device Manager' and hit enter (or select the device manager from the search bar.) In the new window, scroll down to Ports (COM & LPT) and expand the menu. You may see a few options, as shown below.
[Image: pdcm7lP.jpg]

We're using USB for communication, so select the one that says USB and not Bluetooth or anything. Take note of the actual port number beside it (in my case, COM5) and head back to the Arduino IDE.

Back to the top-bar, go to Tools > Port and select the port number you noted from the Device Manager earlier.

To make sure the board is working, go back to Tools and this time click Get Board Info. If you set it up properly, you should get a little pop-up dialog box like below:
[Image: QOV0OCn.jpg]

In my case, the actual board cannot be retrieved because it's a clone from China and not an official board. If you have a clone, don't worry about it not appearing there. As long as you get a dialog box of some sort, you're all set.

If not, run through these steps again or Google for troubleshooting. I don't provide warranty and support.


0x03 - Basic Hashing Logic:

So we have everything set up, meaning it's time to bunker down and start programming!
As mentioned in the intro, the only way to actually 'break' a hash is by testing a million different things to find a hash that matches, and we consider the matched hash to be the 'original' string of data.

So for our Arduino to crack hashes, we need to actually implement the ability to hash things with Arduino!

Now, it's a little more difficult than that: there are tons of hashing algorithms out there. We need to consider a few things:
  • How fast is the hash?
  • How easy is the algorithm to implement?
Arduino isn't the most powerful device in the world. Far from it, actually, even considering the world of microprocessors it's pretty slow. So we want our algorithm to be fast. But fast algorithms also come at a price of security, since we could theoretically check many more hashes in a short amount of time than a more complex and slow algorithm. But, because Arduino is inherently not as powerful enough to hash things quickly, we want something that's pretty fast so that we can still get decent performance from the device.

Thus, I propose MD5 which has been used for over a decade, and is slowly becoming obsolete in the shadows of other great and slower algorithms.

MD5 is great because there are implementations everywhere. RosettaCode has tons, and if you've checked, they have an implementation sample in C. And if you've used Arduino before, you're probably thinking: "Hey! ArduinoLang is almost like C, so if we copy/paste it, it should work! Right?"

And the answer is no, for two reasons: Architecture and Pointer arithmetic. Since hashing is generally done straight in memory (especially in the C sample,) pointer arithmetic varies from x86 architecture (like your computer) and ARM/RISC architecture (Arduino). So we may need to re-implement it.

But fear not! For a great Greek programmer has come to our aide a year in advance, releasing a library that does MD5 hashing for us in Arduino! I present to you: ArduinoMD5, by Tzikis all available on GitHub.

Follow his installation instructions. You may need to alter his instructions if you are on a different OS than Windows. Remember to restart the Arduino IDE after installing it.

To add the library to our sketch, open up the Arduino IDE, and on the first line, link the library:
Code:
#include <MD5.h>
Alternatively, go to Sketch > Include Library > Contributed Libraries > MD5
If you don't see it there, you installed it wrong. Restart your Arduino IDE and try again.

Since Tzikis also included a usage example, I've added some slight modifications and turned his example into a standalone function:
Code:
[color=#333333][size=small][font=Monaco, Menlo, Consolas, ]void hashMD5(char a[]){
 unsigned char* hash = MD5::make_hash(a);
 char *md5str = MD5::make_digest(hash, 16); //Direct example from Tzikis's GitHub.
 for(int i = 0; i < 32; ++i){
   returnVal[i] = md5str[i]; //Assigning the global variable character by character.
 }
 returnVal[32] = 0x00; //Always null-terminate your strings, kids!
 free(hash); //Since we're looping, we need to free up the memory before another iteration.
 free(md5str);
}[/font][/size][/color]

Read the comments, but the function takes in a byte-array (C string) and instead of returning the value (void-type function,) will modify a global variable instead, allowing us to free up the memory again for more iterations.
If we don't free the memory from the algorithm, we risk overflowing the on-board memory on the Arduino.

At the same time, we should also add the global variable listed. Right beneath the import, define returnVal as:
Code:
char returnVal[33];

Now, why 33?
MD5 only spits out 128bit messages in hexadecimal format. A hexadecimal digit uses four bits, so naturally, 128 / 4 is 32.
And why 32 + 1? Because you always need to null-terminate your strings so you don't overflow memory (the program will generally stop reading the array once it encounters a null-byte.) That's why we assign the last byte after the loop in the function to 0x00, a null-byte.

Now, we want to actually send/receive data later on, so we need some sort of communication and actually read the data we get, so we should start setting up our setup() function in the sketch:
Code:
void setup(){
  Serial.begin(9600)
  delay(500) //Half a second delay, just to ensure everything starts up properly.
}

The delay isn't really needed, but I like to include it as a safety net in case something does go terribly wrong.
The 9600 is the baud rate. More on this in the next section.

Finally, we want the program to actually use that fancy MD5 hashing function we made, so in the loop() function, I wrote this:
Code:
void loop() {
  hashMD5("a"); //Hashes the 'a' character.
  Serial.println(returnVal);
  delay(1000); //Unnecessary, but for the sake of readability and a slow Serial monitor.
}

Now verify the code with the checkmark button in the upper-left to make sure it compiles. You may be prompted to save now, if not during upload. If it passes (you may get a warning or two from the library, these are fine,) then upload the code with the button right beside the checkmark. 

To see if the code works, open the Serial Monitor by pressing CTRL+Shift+M or going to Tools > Serial Monitor. If all went well, you should start seeing a weird string in the monitor every second: 0cc175b9c0f1b6a831c399e269772661. This string is the hash value of the character 'a' and it's exactly 32 characters long.

To test this, go to MD5 Hash Generator, type in 'a' (without quotes and no newlines) and hit 'md5'. Success!

But we're not trying to hash 'a' over and over again, we need to be able to input any arbitrary value we want to test, so let's implement some user-input!


0x04 - Hashing a Serial Command:

Now, I know what you're thinking:
"Lain, I don't want to type in thousands of strings myself to try and crack stuff!"
And don't worry, we'll get to automation later on with some more scripting. But to be able to run, we first need to walk. Right now, we're still crawling.

When the Arduino was communicating the hash to us, it was using the Serial System over the COM port we found earlier and set up (COM5 for me.) Serial is great, but it has some specifications we need to be aware about:
  1. Serial implies 'Series'
  2. It has a maximum value.
Serial is derived from the word 'series' and that means that the data it sends is in series, byte-by-byte and bit-by-bit. So we need our program to read the data sequentially.

The max value is defined by the Arduino board: All boards have a Serial buffer that can only store so much incoming data (or outgoing as well, for that matter.) That max value is a total of 64 bytes, or 512 bits. So when we store that data somewhere, we should have a buffer ready. Define a new global variable: buf:
Code:
char buf[64];

We don't need to null-terminate this one, since it's the max value anyway, and if you think about it, you and I don't use 64 character passwords. If you're ultra-paranoid, yours is still probably only around 25-30 characters. That being said, we should also write a function that clears the buffer out after every iteration:
Code:
void clearBuffer(){
  for (int i = 0; i < 64; ++i){
    buf[i] = 0x00;
  }
}
Self-explanatory, I hope.
We need this function because if we put in a password like 'zer0cool', the buffer will fill, get hashed, spit out to the Serial Monitor like before, but then wait to receive a new pass, let's say 'lain' and will then fill the buffer for the first four bytes. The rest of the buffer will still be 'laincool' since there's residual data from the last password.

Thus, we write null-bytes over the whole d*** thing every time.

Now, to actually get data from Serial, we need the help of two functions:
Code:
Serial.readBytesUntil() //Reads the actual data in the buffer
Serial.available() //Returns the number of bytes in the buffer

So, we'll have the loop function check every time whether there is data in the buffer, and if there is, start reading the data. You might propose something like this:

Code:
if(Serial.available() > 0){
  Serial.readBytesUntil();
}

First, I'll call you out on the condition. Anything greater than 0 is considered true, so we could shorten the condition and save a few bytes.
But you have overlooked our Serial definition above. Remember, Serial is sent in a series of bytes, not all at once. On one iteration of the loop, it may return 0, meaning no data, and the next it'll return one because there is a new byte there (or maybe four, or however much was sent in the duration of that loop.)

But it might only read a few bytes, while more data is still being sent. If it returns 4, that's cool, maybe there are currently four bytes in the buffer, but you might have sent 16 bytes, and it's in the process of sending that data, so it'll only read part of that data, hash it, spit it out, and then check again for more data, which leads to more problems.

So instead, for the time being, let's add a short delay to make sure all the data gets sent before we try to read it. While using proper function arguments, we'll end up with this kind of main loop():
Code:
void loop() {
  clearBuffer();
  if(Serial.available()){
      delay(10);
      Serial.readBytesUntil('\n', buf, Serial.available());
      Serial.println(buf);
      hashMD5(buf);
      Serial.println(returnVal);
  }
}

In the readBytesUntil() function, we are using '\n' as a terminator; once the program reads this byte, it will discard it and consider it the end of the string. We will need this later, but it's also helpful right now, as seen in the demo below.

I've bumped up the baud rate (Serial.begin() in setup() ) to 115200. The baud rate is the rate at which data is sent, in bits. So 115200 bits per second.
Since we're sending a maximum of 512 bits at a time, 512 / 115200 = 0.0044...
So about four/five milliseconds should be enough for transferring all the data. To play it safe, I added a delay of 10 milliseconds, which we can optimize later.

Our final program once put altogether will look like this:

Code:
#include <md5.h>
char returnVal[33]; //An MD5 hash is ALWAYS 32 characters long, or 128bit. Add an extra byte for null-termination.
char buf[64]; //The max Serial buffer is 64 bytes.

void setup() {
  pinMode(2, INPUT_PULLUP); //Unnecessary at the moment, will be needed later.
  Serial.begin(115200);
  delay(1000); //A second to start up properly.
}

void clearBuffer(){
  for (int i = 0; i < 64; ++i){
    buf[i] = 0x00;
  }
}

void hashMD5(char a[]){
  unsigned char* hash = MD5::make_hash(a);
  char *md5str = MD5::make_digest(hash, 16); //Direct example from Tzikis's GitHub.
  for(int i = 0; i < 32; ++i){
    returnVal[i] = md5str[i]; //Assigning the global variable character by character.
  }
  returnVal[32] = 0x00; //Always null-terminate your strings, kids!
  free(hash); //Since we're looping, we need to free up the memory before another iteration.
  free(md5str);
}

void loop() {
  clearBuffer();
  if(Serial.available()){
      delay(10);
      Serial.readBytesUntil('\n', buf, Serial.available());
      Serial.println(buf);
      hashMD5(buf);
      Serial.println(returnVal);
  }
}

Upload the sketch and wait a second.

Open up the Serial monitor again (CTRL+SHIFT+M) and instead, you won't see the same hash running down the screen. In fact, the screen will be empty. Notice the little message box at the top. Type in 'a' and press Send.

You should get the same hash value as before. Try it with different letters, or even full words, or even sentences (below 64 bytes, though!) Notice the bottom of the screen a little dropdown menu with the word "Newline". That's the '\n' terminating character we used before for readBytesUntil(). Changing it may break the program, but we will account for it later, since we need a terminator of some sort anyway.

Now we have a cool hashing machine, and it's pretty fast at actually hashing stuff! And, like I promised at the beginning of this stage, we're going to automate the process so you're not typing stuff in over and over again. User-Input really means Machine-input for where we're going next.


0x05 - Communicating with Python:

Fire up PyCharm (or your favourite text editor) because we're starting the automation process.

Make a new file/project and call it something like script.py or main.py. It really doesn't matter. add two imports: sys and time as so:
Code:
import time
import sys

We need sys for the exit function and we need time for the delays and ensuring synchronization between serial I/O (as we did in the Arduino sketch.)

Now, we need a way for Python to interact with the Serial device, ie. Arduino, so I found a relatively well-maintained library for doing just that (and it supports Python3.) It's called pyserial.

If you installed pip with Python, then open up a terminal and type in:
Code:
pip install pyserial
If you get an error, make sure that pip is installed somewhere, and also make sure you added Python to your PATH from the installer. Again, no support if these don't work for you or you have no idea what I'm talking about. Google it.

When using this library, despite it's name being pyserial, we imposrt it as just 'serial'.
Code:
import time
import sys
import serial

We should also specify our COM port and baud rate as we did in the Arduino sketch/IDE:
Code:
baud = 115200
port = 'COM5'

115200 is the baud rate we used in the sketch, so naturally we want it to be the same. COM5 is my port. Remember to specify it as a string (use the quotes.) Change the port if necessary to your own.

To actually use serial communications, we need to create a serial object from the library and by following the official pyserial docs, we instantiate the object like this:

Code:
ser = serial.Serial(port, baud, timeout=1)
time.sleep(1)

The only really important parts are the port and baud rate, everything else is secondary. The timeout is there to make sure that the device has at least a second to respond, else it'll be a failed connection. I also add a sleep of one second to make sure that the device isn't in the process of timing out before we do anything else.

Add one more line to print the object, and run the script:
Code:
print(ser)

By instantiating the serial object, the connection is automatically opened and we can start using it, no need for more functions. So after the sleep, we just print the object data and you should see a property that looks like this:
Code:
open=True

If you did, congratulations! You can now communicate with the Arduino and we can move on!
If not, check your COM port and make sure you changed the baud rate in the Arduino (or change it to whatever you'd like, as long as the number is the same in both the script and the sketch.) No more support.

Now, we're going to use some actual serial functions in python:
Code:
Serial.write() // Same thing as Arduino Serial.print()
Serial.flushOutput() //Clears the output serial buffer
Serial.read_until() // Same thing as Arduino Serial.readBytesUntil()
Serial.close() //It's always good manners to close a connection when you're done with it.

So let's put it all together!
Code:
import serial
import time
import sys

port = 'COM5'
baud = 115200

ser = serial.Serial(port, baud, timeout=1)
time.sleep(1)
ser.write(b'a\n') #You can only write bytes data, so we do the conversion from the string 'a\n' 
ser.flushOutput() #Once sent, PySerial does not clear the output buffer on its own, so we need to do it manually.
time.sleep(0.010) #Give the device ten seconds to receive some data back
hashVal = ser.read_until(b'\n', 32) #Will read until it either finds a newline character or until it reaches the maximum of 32 bytes.
print(hashVal) #prints the received data
ser.close() #Good manners
sys.exit()

The comments should be verbose enough, but I'll explain the write() part a bit more.
It's not pythonic. I know. F**k your PEP8 standards, I don't care. I'll change it later, I promise. What's important is that we add that newline character, '\n'. It's important because that's the same terminator we used in the sketch on the arduino:
Code:
Serial.readBytesUntil('\n', buf, 64)

So it'll stop reading the buffer once it reaches that \n, and won't include it, meaning faster read times giving us more time to compute the hash.
Since we are only sending 32 bytes at a time from the Arduino, we only need to receive 32 bytes, hence the value defined in read_until().

Run the code, and if all goes well, you should have this output:
Code:
b'0cc175b9c0f1b6a831c399e269772661'
Because we're printing bytes values, it's encapsulated in the b'', so don't worry about that. We'll fix that later.

One more thing we want to add. We're doing this whole thing with Python for the sake of automating the process, so let's actually automate it with a hard-coded list. Let's change up the script really quick:

Code:
import serial
import time
import sys

port = 'COM5'
baud = 115200
passes = ['a','b','c','d','e'] # List of things to hash

ser = serial.Serial(port, baud, timeout=1)
time.sleep(1)
for item in passes: # loops around for each item in the list
    ser.write(item.encode()) # a more pythonic way to encode to bytes
    ser.flushOutput()
    time.sleep(0.020) #Give the arduino a few more milliseconds for synchronicity
    hashVal = ser.read_until(size=32) #We don't really 'need' the terminator if we only read 32 bytes.
    ser.flushInput() #Safety of clearing the buffer once we read it.
    print(hashVal.decode('ascii')) # a more pythonic way of decoding bytes to text
ser.close()
sys.exit()

And now, the output should be this:
Code:
0cc175b9c0f1b6a831c399e269772661
92eb5ffee6ae2fec3ad71c777531578f
4a8a08f09d37b73795649038408b5f33
8277e0910d750195b448797616e091ad
e1671797c52e15f763380b45e841ec32

Each character gets passed to the arduino and hashed!

We don't want to hardcode our list of words to try, so let's add some logic to open a file full of words, parse it, and while we're at it, add a check to see if we match a given hash to break.


0x06 - Automating the Process:

I'll cut it short since this is starting to get lengthy. To open a file, we don't need any additional libraries, just this little code snipped at the top with our variable declarations:
Code:
file = open("list.txt", "r")
if file.mode != "r":
    print("Unable to open list.txt in current directory!")
    sys.exit()
lines = file.readlines()
This opens the file and splits it based on each line. Each line in our file should be one word/character to hash.

We should also add a declaration for a hash to crack:
Code:
toCrack = '8277e0910d750195b448797616e091ad'

Since we're now using a different list, we should modify the loop, and actually add a conditional to check of the received hash is the same as the toCrack hash. Here's the full code:
Code:
import serial
import time
import sys

port = 'COM5'
baud = 115200
file = open("list.txt", "r")
if file.mode != "r":
    print("Unable to open list.txt in current directory!")
    sys.exit()

lines = file.readlines()
toCrack = '8277e0910d750195b448797616e091ad'

ser = serial.Serial(port, baud, timeout=1)
time.sleep(1)

for line in lines:
    ser.write(line.encode())
    ser.flushOutput()
    time.sleep(0.020)
    hashVal = ser.read_until(size=32)
    ser.flushInput()
    if (hashVal.decode('ascii') == toCrack):
        print("Found! -- " + line)
        ser.close()
        sys.exit()
ser.close()
sys.exit()

To make this script work, put a file in the same directory as the script you made and name it 'list.txt'. make the file read as follows:
Code:
a
b
c
d
e

See the image below:
[Image: 39T5jtI.jpg]

Once the file is made and in the same directory (with the same name,) run the script. You should now get this output.
Code:
Found! -- d
Congratulations! We've finally made a hash cracker with full wordlist support! The prototype works and can be considered complete now, except me being me and wanting optimization, I took it another step further.


0x07 Making it Fast

Remember those delays we added everywhere to make sure it would synchronize properly? Those are eating tons of time. Taking the largest one of 0.020s in the Python script, we could only compute a total of 50 hashes per second, which, although sounds high, is actually incredibly small. To increase the speed, the first place we're going to look is to take away the delays.

To fix the Arduino sketch and its 10ms delay, I've opted for this solution:
Code:
void loop() {
  clearBuffer();
  if(Serial.available()){
      Serial.readBytesUntil('\n', buf, 64);
      if(buf){
          hashMD5(buf);
          Serial.print(returnVal);
      }
  }
}

Not much different, and it makes sure the buffer really is empty before trying again. I've also changed Serial.println() to Serial.print() so it doesn't send the newline characters \r\n.

And to fix the Python delays, I've opted for this:

Code:
for line in lines:
    ser.write(line.encode())
    ser.flushOutput()
    while ser.inWaiting() < 32:
        continue
    hashVal = ser.read_until(size=32)
    if hashVal.decode('ascii') == toCrack:
        print("Found! -- " + line)
        ser.close()
        sys.exit()
    ser.flushInput()
ser.close()
sys.exit()

The script will actually stop at the while loop until there are at least 32 bytes and it'll only read 32 bytes at a time. The flushInput() probably isn't necessary anymore since the buffer should be clear after the read and have no terminating characters, but it's still there for safety.


0x08 - Conclusion:

Now, the project will crack hashes at approximately 150 hashes per second, or about three times faster than before. That's great to hear. The only remaining time to optimize upon is:
  1. The optimization of the hashing algorithm implementation
  2. The transfer time over Serial.
Now, 1. isn't so easy to fix, but 2. could be made faster by simply increasing the baud rate. Since I wanted to keep this project as contained as possible (ie. no shields, no wires, no bullshit,) I didn't use any other peripherals. But if you stored a wordlist on an SD card and used a microSD shield, you could get rid of 99% of the Serial communication time by only sending the hash to crack over serial, then waiting on a response from the Arduino, while the Arduino pulls words to hash from the SD card and does the computations, only sending back a message if it found or didn't find the hash, and what the original string is. This is where you'd go in the future for optimization.

Overall, it was a fun project, and even I learned a lot about Serial communications over the duration. It's a shame that Instructables unpublished my guide, but I guess that's just an opportunity to bring one of my other ideas to life, and the opportunity to write everything here instead (congrats, you're now the only website online that is hosting this guide publicly!)

I've attached the final files as well, the Sketch.ino Arduino sketch and my main.py Python script.

If you make it or if you optimize it, let me know! Learn from it, build it, build on it!

~Lain
Reply
#2
This is epic Lain!
hey question, can you cluster arduino's together to distribute the workload and crack things faster?

I know people have clustered pi's so I'm kinda curious as to what would be faster, a pi cluster or an arduino cluster at cracking hashes.

mostly I'm thinking that arduino may be the better route for modules to hook up to my tablet instead of rigging a pi-top, and having some sort of cluster box I could plug in and run a decoding algorithm too would be way more useful if the overhead of the OS on the pi is taken out of the equation.
"I reject your reality and subsitute my own." - Adam Savage, Mythbusters
[Image: 5.jpg]
Reply
#3
(October 22nd, 2019 at 5:35 AM)SpookyZalost Wrote: This is epic Lain!
hey question, can you cluster arduino's together to distribute the workload and crack things faster?

I know people have clustered pi's so I'm kinda curious as to what would be faster, a pi cluster or an arduino cluster at cracking hashes.

mostly I'm thinking that arduino may be the better route for modules to hook up to my tablet instead of rigging a pi-top, and having some sort of cluster box I could plug in and run a decoding algorithm too would be way more useful if the overhead of the OS on the pi is taken out of the equation.

Yes and no. Observe the fritzing schematic I threw together:
[Image: e777e66f7bcf7af9d5af65bfbb1c4c34.png]

Essentially what I do here is just wire up a couple of Arduino Nano's (AtMEGA328p, like the UNO) to an Arduino MEGA2560. In particular, each nano is inversely connected by the RX/TX pins (ie. MEGA2560 RX is connected to Nano TX and vice versa.)

RX and TX pins are basically just for the sake of emulating Serial communications on a single pin. Data is transferred the same way and at the same speed as defined by the baud rate. If you uploaded each of the above sketch to the Nano's then you now have the program set for distributed cracking, and you can write yourself a program for the MEGA2560 that will read from Serial inputs (MEGA2560 has up to three) and will send data over the RX/TX pins respectively to the Nano, receive the data, and send the hash back over Serial to the host computer. Since the MEGA2560 supports pseudomultithreading (ie. multiple loop() functions) then you can actually make the MEGA part run in parallel pretty well and reduce bottlenecking.

But the problem also lies in that explanation above: since you're still transferring data over a Serial-based solution, the transfer speeds are still going to impact the total hash rate, especially when you're essentially doubling it by forwarding the data through a middleman of a device. Ideally, reducing these will be key for getting better performance.
Reply
#4
d***, alright, thanks for the really detailed explanation, that was incredibly helpful.
"I reject your reality and subsitute my own." - Adam Savage, Mythbusters
[Image: 5.jpg]
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [Project] [DIY/WIP] Hardware Keylogger (with Arduino) Lain 11 8,853 March 4th, 2020 at 7:57 PM
Last Post: Lain



Users browsing this thread: 1 Guest(s)

Dark/Light Theme Selector

Contact Us | Makestation | Return to Top | Lite (Archive) Mode | RSS Syndication 
Proudly powered by MyBB 1.8, © 2002-2024
Forum design by Makestation Team © 2013-2024