# STL 1 Assignment 1
Ivan Christian
1003056
## 3
```python=
import hashlib
hashed = hashlib.md5("password".encode()).hexdigest()
print(hashed)
print(len(hashed)) # 32 chars 128 bits
hashed = hashlib.sha1("password".encode()).hexdigest()
print(hashed)
print(len(hashed)) # 40 chars
hashed = hashlib.md5("passwordpassword".encode()).hexdigest()
print(hashed)
print(len(hashed)) # 32 chars 128 bits
hashed = hashlib.md5("passwordpasswordpasswordpassword".encode()).hexdigest()
print(hashed)
print(len(hashed)) # 32 chars 128 bits
```
Results
```
md5 : password 5f4dcc3b5aa765d61d8327deb882cf99 5f4dcc3b5aa765d61d8327deb882cf99 32
32
sha1 : password 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8 40
40
md5 : passwordpassword 9dbb300e28bc21c8dab41b01883918eb 9dbb300e28bc21c8dab41b01883918eb 32
32
md5 : passwordpasswordpasswordpassword e8562871ffe80d5b4572ea660a0a764a 32
```
As seen, the length changes from 32 to 40 only when the hash function is changed. Otherwise there is no change in the length of the hashed value. So it does not depend on the length of input.
## 4
```python=
'''
md5_lab1.py
Dictionary Attack and Brute Force Attack For Part 4
'''
import os
import sys
import hashlib
import string
import argparse
import timeit
def read_file(student_id, word_text):
'''
Read and Clean the txt file
'''
wordpath = os.path.join(word_text)
filepath = os.path.join('.', student_id)
w = open(wordpath, 'r')
f = open(filepath, "r")
cleaned_hash_list = [ i.replace('\n', '') for i in list(f) ]
cleaned_word_list = [ i.replace('\n', '') for i in list(w) ]
return cleaned_word_list, cleaned_hash_list
def save_list_to_file(lst, filename, time_taken):
'''
Save word-hash pair to a txt
'''
with open(filename, 'w') as file:
for item in lst:
file.write(str(item) + '\n')
file.write(f'Time taken : {time_taken}')
def dictionary_attack(ptxt, htxt):
d_list = []
for word in ptxt:
hashed = hashlib.md5(word.encode()).hexdigest()
if hashed in htxt:
out = f'{word} : {hashed}'
d_list.append(out)
print(out)
return d_list
def brute_force(ptxt, htxt):
b_list = []
alphabet_list = list(string.ascii_lowercase + string.digits)
for i1 in alphabet_list:
for i2 in alphabet_list:
for i3 in alphabet_list:
for i4 in alphabet_list:
for i5 in alphabet_list:
res_str = f'{i1}{i2}{i3}{i4}{i5}'
hashed = hashlib.md5(res_str.encode()).hexdigest()
if hashed in htxt:
out = f'{res_str} : {hashed}'
b_list.append(out)
print(out)
return b_list
def main():
parser = argparse.ArgumentParser(description='Inputs arguments for the Assignment')
parser.add_argument('-i', '--input', type = str)
parser.add_argument('-w', '--word', type = str)
parser.add_argument('-o', '--output', type = str)
parser.add_argument('-m', '--mode', type=int, choices=[0, 1], help="0: dictionary attack, 1: brute force")
#d_output = 'dictionary_attack.txt'
#b_output = 'brute_force_attack.txt'
args = parser.parse_args()
student_id = args.input
word_text = args.word
output = args.output
mode = args.mode
ptxt, htxt = read_file(student_id, word_text)
if mode == 0:
print('-'*30, 'Dictionary Attack', '-'*30)
starttime = timeit.default_timer()
d_list = dictionary_attack(ptxt, htxt)
time_taken = timeit.default_timer() - starttime
save_list_to_file(d_list, output, time_taken)
print(time_taken)
elif mode == 1:
print('-'*30, 'Brute Force Attack' ,'-'*30)
starttime = timeit.default_timer()
b_list = brute_force(ptxt, htxt)
time_taken = timeit.default_timer() - starttime
save_list_to_file(b_list, output,time_taken)
print(time_taken)
if __name__ == '__main__':
main()
```
-------------
#### How to run:
1. python3 md5_lab1.py -i 1003056-hash15.txt -w words5.txt -o dictionary_attack.txt -m 0
2. python3 md5_lab1.py -i 1003056-hash15.txt -w words5.txt -o brute_force_attack.txt -m 1
-------------
Results
```
-- Mode 0 : Dictionary Attack
------------------------------ Dictionary Attack ------------------------------
utone : 114eab2b418ff3926c542ef6efe2a350
lyasl : 831f2b7b4ff9ad176ac8c5a67f1c5cb1
seusi : 0f44a89aa198dd685513b4dedea44589
adsqu : c6bb913b3d2909396b66ccf90b8049f9
dkows : 2b6b9f760242e66925060d3bd1a19230
eoryn : ddc407fd5fec41be724f4d1156ae7141
cocla : 33bdddc3ba5acb4965e88243cce0cb2b
indkn : 965597b00d4525bb9dbe8c9f175f4ab8
Time taken : 1.101025796000613
-- Mode 1 : Brute Force Attack
------------------------------ Brute Force Attack ------------------------------
adsqu : c6bb913b3d2909396b66ccf90b8049f9
ai8ja : 58c854c082728a84c71da4a19315251c
cocla : 33bdddc3ba5acb4965e88243cce0cb2b
dc1rt : 31c2bb840520b2ddaebf183dfa1d5060
dkows : 2b6b9f760242e66925060d3bd1a19230
eoryn : ddc407fd5fec41be724f4d1156ae7141
ezies : caf129881ae3f2f0fa04fe8a0629eb51
indkn : 965597b00d4525bb9dbe8c9f175f4ab8
ku5si : b815d63b68bd6dd17273da2f1faf0a81
lyasl : 831f2b7b4ff9ad176ac8c5a67f1c5cb1
sbteo : 584cd273aee149f98689440e0ce9cfd6
seusi : 0f44a89aa198dd685513b4dedea44589
sly7r : 8cda1953eba5b47a30923e69cb050b7b
utone : 114eab2b418ff3926c542ef6efe2a350
yi9ac : 419ab21691b5f71f88d8746889505da0
Time taken : 152.00617839099868
```
##### Dictionary Attack

Time taken : 1.101025796000613 s
##### Brute Force Attack txt

Time taken : 152.00617839099868 s
Dictionary attack takes less time than the brute force attack
## 5
We will be using rtgen on
rtgen md5 loweralpha-numeric 5 5 0 3800 600000 0
We were given a 15 character length hash values in 1003056-hash15.txt
Initial chain lengths: 3800
initial chain number : 600000
rtsort . is done to sort the rainbow table



All 15 of 15 hashes were found.
To decrease the ratio of rainbow from 40:1 to 20:1:
$chain number=600000/2=$ $300000$

To decrease the ratio of rainbow from 40:1 to 10:1:
$chain number=600000/4=$$150000$

To decrease the ratio of rainbow from 40:1 to 5:1:
$chain number=60000/8=$$75000$

The same commands were done in kali to generate the rainbow tables and the tables are coppied out to the windows machine.
The following are the results from the new rainbow tables:
for 20 : 1:
rcrack . -l 1003056-hash15.txt


for 10 : 1:
rcrack . -l 1003056-hash15.txt


for 5 : 1 :
rcrack . -l 1003056-hash15.txt


```csvpreview
Ratio,40 : 1, 20 : 1, 10 : 1, 5: 1
Time to generate, roughly 3 mins, roughly 1 min 30 s, 51.6 s, 25.3 s
Chain length,600000,300000,150000, 75000
ptext found,15,15,15,14
Time taken to crack, 4.52 s,4.53 s, 4.23 s, 4.72 s
Total Time Taken, roughly 3 mins, roughly 1 min 34 s, 55.8 s, 30.0 s
```
Based on the experiment, we can see that 5:1 rainbow table doesnt crack all the hashes whereas the 40:1, 20:1, and 10: 1 cracks all of them. We can conclude that based on size alone 10: 1 would be enough to crack all the hashes for this case, hence making it the most efficient in the experiment.
## 6
```python=
'''
md5_lab1_salted.py
Generate salted hash and password
'''
import os
import sys
import hashlib
import string
import argparse
import random
import timeit
from random import randint
def read_file(student_id, word_text):
'''
Read and Clean the txt file
'''
wordpath = os.path.join(word_text)
filepath = os.path.join('.', student_id)
w = open(wordpath, 'r')
f = open(filepath, "r")
cleaned_hash_list = [ i.replace('\n', '') for i in list(f) ]
cleaned_word_list = [ i.replace('\n', '') for i in list(w) ]
return cleaned_word_list, cleaned_hash_list
def save_list_to_file(lst, filename):
'''
Save word-hash pair to a txt
'''
with open(filename, 'w') as file:
for item in lst:
file.write(str(item) + '\n')
def dictionary_attack(ptxt, htxt):
d_list = []
for word in ptxt:
hashed = hashlib.md5(word.encode()).hexdigest()
if hashed in htxt:
out = f'{word} : {hashed}'
d_list.append(out)
print(out)
return d_list
def hash_words(salted_list):
'''
Hashes word
'''
salted_hashed = []
for word in salted_list:
hashed = hashlib.md5(word.encode()).hexdigest()
salted_hashed.append(hashed)
return salted_hashed
def brute_force(ptxt, htxt):
b_list = []
alphabet_list = list(string.ascii_lowercase + string.digits)
for i1 in alphabet_list:
for i2 in alphabet_list:
for i3 in alphabet_list:
for i4 in alphabet_list:
for i5 in alphabet_list:
res_str = f'{i1}{i2}{i3}{i4}{i5}'
hashed = hashlib.md5(res_str.encode()).hexdigest()
if hashed in htxt:
out = f'{res_str} : {hashed}'
b_list.append(out)
print(out)
return b_list
def main():
parser = argparse.ArgumentParser(description='Inputs arguments for the Assignment')
parser.add_argument('-i', '--input', type = str)
parser.add_argument('-w', '--word', type = str)
parser.add_argument('-o', '--output', type = str)
parser.add_argument('-m', '--mode', type=int, choices=[0, 1], help="0: dictionary attack, 1: brute force")
#d_output = 'dictionary_attack.txt'
#b_output = 'brute_force_attack.txt'
args = parser.parse_args()
student_id = args.input
word_text = args.word
output = args.output
mode = args.mode
ptxt, htxt = read_file(student_id, word_text)
print('-'*30, 'Brute Force Crack' ,'-'*30)
starttime = timeit.default_timer()
b_list = brute_force(ptxt, htxt)
ALPHABET = "abcdefghijklmnopqrstuvwxyz"
salted_pass = [ word.split(' : ')[0] + random.choice(ALPHABET) for word in b_list]
salted_hashed = hash_words(salted_pass)
print('-'*30, 'Saving Salted Hashed', '-'*30)
save_list_to_file(salted_pass, 'pass6.txt')
save_list_to_file(salted_hashed, 'salted6.txt')
for w,h in zip(salted_pass,salted_hashed):
print(w,h)
if __name__ == '__main__':
main()
```
Salted Passwords
```
adsqur
ai8jac
coclap
dc1rtv
dkowsu
eorync
eziesq
indknr
ku5sik
lyasli
sbteoe
seusix
sly7rk
utones
yi9acs
```
Salted Hash
```
6bf87ac1f82f0226ba6613ee17b82f13
3df27eeae6aebae014adadd2d0ae5595
9da051cf6e4c2900cef17c2035118d5e
7175fa98693f6ada755cd1b8e494f6be
6ba0ac4bc970843f1b2f4c1f42ef7ff2
374d2389e78376ee47dd23013855f6f7
377942bb105f02eb8cb66b866495e734
61bf9b1504d57b2e255d0b9473b634d3
d38800841041f5550d55fbe6379525d9
a50f0b87832173485cd646c6d2eb5765
90c748b7d93d8cc8e64a9c3613708528
c20d42bc44ca762e51fab68e38b1703a
92185f6b786b87c2d603616a3c7ae662
7b6ec17fd30d43b7041ddfee63489c19
72b135df4c0df30719b73bef1f16ef96
```
Due to the salt, the character length of the plaintext has increased from 5 to 6. Thus the total number of combinations has increased to 2 billion.
$36^6 = (2,176,782,336)$
$chain number=(2,176,782,336 × 5)/3800=2,864,187$
Newly saved salted password and the salted hashed. Save in the salted6.txt and pass6.txt:

New parameters for the rtgen:
rtgen md5 loweralpha-numeric 6 6 0 3800 2864187 0
rtsort .

Cracking salted6.txt with the parameters
rcrack . -l salted6.txt

A change in the parameter:
rtgen md5 loweralpha-numeric 6 6 1 3800 2864187 0
rtsort .

rcrack . -l salted6.txt

```csvpreview
Component,Non-Salted, Salted (0 3800 2864187),Salted (1 3800 2864187)
Character space plain-text, 5, 6,6
Total combinations, 60 M, 2 B, 2 B
Total plain text in rainbow table, 604 M, 20 B, 20 B
Time taken to crack the hashes, 4.52 s, 7.27 s, 8.30 s
Time taken to generate rt,roughly 140 s,roughly 880s, roughly 1000 s
```
We know that password salting is adding on a random character, which can be alphanumeric as is the case for this exercise, which makes the password cracking harder due to the extra character in the password. The main reason
As we can see the difference in generation time and the space required (which correlates to the total number of plaintext in the rt) is large. It can also be seen that the it is slightly harder to crack the hashes when the passwords are salted (from all passwords being cracked to only 14 being cracked as opposed ot 15), making it more slightly more secure. We can assume that if the salting is alphanumeric and contains both uppercase and lower case, it will be harder to crack.
For this rt generation, I am using the kali linux provided, as such it takes a lot of time to generate 1 rt when compared to using a machine with better specs.
## 7
### 7.1
Generate lowercase rule : `mp64 $?l -o lowercase.rule`
See the list of things in lowercase rule : `type lowercase.rule`
Command for running hashcat
`hashcat64 -m 0 -D 2 -o cracked.txt salt6.txt words5.txt -O -r lowercase.rule`

Checking how many passwords can be cracked:

we can see that 8/15 passwords were cracked. Since it has not cracked all the password
As such, an addition to the rules is done in order to crack more password:
Addition of mask:
- We know that the salt is lowercase alphabets
- We also know that the length of the salted password is 6 chars
`hashcat64 -D 2 -m 0 -a 3 -o bruteforce_cracked.txt salted6.txt ?a?a?a?a?a?l -O --potfile-disable`
Note that potfile disable is used in order to not crack previously cracked password.

Cracking the password using the specified ?a commands( which is used for all types), all the passwords are cracked. Since we know that the last char is lowercase alphabet, we can use ?l to reduce the computation necessary.
Comparing with the custom python script that was previously run, the following are obtained:
This is the code for the cracking
```python=
'''
md5_lab1.py
'''
import os
import sys
import hashlib
import string
import argparse
import random
import timeit
from pprint import pprint
from random import randint
def read_file(student_id, word_text):
'''
Read and Clean the txt file
'''
wordpath = os.path.join(word_text)
filepath = os.path.join(student_id)
w = open(wordpath, 'r')
f = open(filepath, "r")
cleaned_hash_list = [ i.replace('\n', '') for i in list(f) ]
cleaned_word_list = [ i.replace('\n', '') for i in list(w) ]
return cleaned_word_list, cleaned_hash_list
def save_list_to_file(lst, filename):
'''
Save word-hash pair to a txt
'''
with open(filename, 'w') as file:
for item in lst:
file.write(str(item) + '\n')
def dictionary_attack(ptxt, htxt):
d_list = []
for word in ptxt:
hashed = hashlib.md5(word.encode()).hexdigest()
if hashed in htxt:
out = f'{word} : {hashed}'
d_list.append(out)
print(out)
return d_list
def hash_words(salted_list):
'''
Hashes word
'''
salted_hashed = []
for word in salted_list:
hashed = hashlib.md5(word.encode()).hexdigest()
salted_hashed.append(hashed)
return salted_hashed
def brute_force(ptxt, htxt):
print('REACHED HERE')
b_list = []
alphabet_list = list(string.ascii_lowercase + string.digits)
for i1 in alphabet_list:
for i2 in alphabet_list:
for i3 in alphabet_list:
for i4 in alphabet_list:
for i5 in alphabet_list:
res_str = f'{i1}{i2}{i3}{i4}{i5}'
hashed = hashlib.md5(res_str.encode()).hexdigest()
if hashed in htxt:
out = f'{res_str} : {hashed}'
b_list.append(out)
print(out)
return b_list
def brute_force(ptxt, htxt):
print('REACHED HERE')
b_list = []
alphabet_list = list(string.ascii_lowercase + string.digits)
for i1 in alphabet_list:
for i2 in alphabet_list:
for i3 in alphabet_list:
for i4 in alphabet_list:
for i5 in alphabet_list:
for i6 in alphabet_list:
res_str = f'{i1}{i2}{i3}{i4}{i5}{i6}'
hashed = hashlib.md5(res_str.encode()).hexdigest()
if hashed in htxt:
out = f'{res_str} : {hashed}'
b_list.append(out)
print(out)
return b_list
def main():
parser = argparse.ArgumentParser(description='Inputs arguments for the Assignment')
parser.add_argument('-i', '--input', type = str)
parser.add_argument('-w', '--word', type = str)
parser.add_argument('-o', '--output', type = str)
parser.add_argument('-m', '--mode', type=int, choices=[0, 1], help="0: dictionary attack, 1: brute force")
args = parser.parse_args()
student_id = args.input
word_text = args.word
output = args.output
mode = args.mode
salted_pass, salted_hashed = read_file(student_id, word_text)
print(salted_pass, salted_hashed)
starttime = timeit.default_timer()
print('-'*30, 'Brute Force Crack' ,'-'*30)
salted_b_list = brute_force(salted_pass, salted_hashed)
time_taken = timeit.default_timer() - starttime
print(salted_b_list)
print(time_taken)
if __name__ == '__main__':
main()
```
We can see that the brute-force using custom python script on the salted passwords take way longer than when using hashcat. hashcat takes around 1 minute to crack all the password.

Total time taken 2919 s
As such, between the custom python script or hashcat, hashcat is a more preferable method for my case.
### 7.2
Based on previous timing from the rainbow table section
```csvpreview
Component,Hashcat, Rainbow Table
Time taken to crack the hashes, 13 s, 7.27 s
Time taken to generate rt,-,roughly 880s
```
###### hashcat

###### Rainbow Table

Hashcat cracking only needs to run the hashcat application while rainbow table needs to be generated first. If the cracking is only done once, then it is more efficient to use hashcat as the total time taken to do it is less than the rainbow table. however, if the cracking is done multiple times, on average it would be more effective to use a rainbow table.
## 8
In order to crack the hashes in `hash-comptetition.txt`, some processing is necessary to seperate the weak, moderate, and strong passwords. Each of the categories have their own files: weak has `hash-weak.txt`, moderate has `hash-moderate.txt`, strong has `hash-strong.txt`:
Cleaning out the txt files can be seen i the following:
```python=
import os
import sys
def read_file(hashfile):
'''
Read and Clean the txt file
'''
filepath = os.path.join(hashfile)
f = open(filepath, "r")
cleaned_txt_list = [ i.split(' ')[1].replace('\n', '') for i in list(f) ]
return cleaned_txt_list
def save_list_to_file(lst, filename):
'''
Save word-hash pair to a txt
'''
with open(filename, 'w') as file:
for item in lst:
file.write(str(item) + '\n')
def main():
files_type = ['weak', 'moderate', 'strong']
for t in files_type:
files_path = f'hash-{t}.txt'
fls = read_file(files_path)
save_list_to_file(fls, f'cleaned-{files_path}')
if __name__ == '__main__':
main()
```
The cleaned files are named `cleaned-hash-(type).txt` where the types are weak, moderate or strong.
### 8.1
Cracking the weak passwords, we will be using the following command to do a recon on what kind of passwords we are dealing with:
`hashcat64 -D 2 -m 0 -a 3 -o weak-hash-cracked.txt cleaned-hash-weak.txt ?a?a?a?a?a?a -O --potfile-disable`

```
4297f44b13955235245b2497399d7a93:123123
e10adc3949ba59abbe56e057f20f883e:123456
72b302bf297a228a75730123efef7c41:banana
fe01d67a002dfa0f3ac084298142eccd:orange
c822c1b63853ed273b89687ac505f9fa:google
d8578edf8458ce06fbc5bb76a58c5ca4:qwerty
9443b0fceb8c03b6a514a706ea69df0b:donkey
8621ffdbc5698829397d97767ac13db3:dragon
e99a18c428cb38d5f260853678922e03:abc123
981d304c3f23f463adfefc42028f7f0c:zyx987
5ebe2294ecd0e0f08eab7690d2a6ee69:secret
7f59a125a3f57ff02c3691b7a829b837:cuvant
4297f44b13955235245b2497399d7a93:123123
e10adc3949ba59abbe56e057f20f883e:123456
72b302bf297a228a75730123efef7c41:banana
fe01d67a002dfa0f3ac084298142eccd:orange
c822c1b63853ed273b89687ac505f9fa:google
d8578edf8458ce06fbc5bb76a58c5ca4:qwerty
9443b0fceb8c03b6a514a706ea69df0b:donkey
8621ffdbc5698829397d97767ac13db3:dragon
e99a18c428cb38d5f260853678922e03:abc123
981d304c3f23f463adfefc42028f7f0c:zyx987
5ebe2294ecd0e0f08eab7690d2a6ee69:secret
7f59a125a3f57ff02c3691b7a829b837:cuvant
```
We are assuming that there exists some 6 char password that can be cracked and from seeing the the available cracked passwords, we can see that the passwords are commonly used passwords. As such, to further crack the passwords we can use the rockyou.txt, which contains the commonly used passwords. This is a type of dictionary attack that is done using an opensource common list.
The following command is run to crack the weak passwords:
`hashcat64 -D 2 -m 0 -o weak-hash-cracked.csv cleaned-hash-weak.txt rockyou.txt -O --potfile-disable`

### 8.2
A dictionary attacak is also run on the moderate password set.
`hashcat64 -D 2 -m 0 -o moderate-hash-cracked.csv cleaned-hash-moderate.txt rockyou.txt -O --potfile-disable`

In order to get more cracks, we have devised the following:
- Instead of using a dictionary, we use a mask in order to brute force match the passwords. This will hopefully match more passwords since rather than checking whether it is in the dictionary, it checks for the patterns instead.
The following command is run in order to test the commands:
`hashcat64 -D 2 -a 3 -m 0 -o mask-moderate-cracked.csv cleaned-hash-moderate.txt masks\rockyou-7-2592000.hcmask -w 3 --potfile-disable -O`
As this is a brute force attack, This takes some time and the following results are obtained.

This brute force mask approach approach has costed me about 4 hours and has heated up my gpu to 90C in the process. Only 10 out of 67 has been obtained and out of fear that my gpu would break I decided to stop the cracking. As it has not done a signifcantly better job than doing a dictionary attack using `rockyou.txt`, I would feel that the trde off between the time and resources would not be worth it. It should be noted that the content of the cracked hash is different from what is
Although it does possibly increase the number of passwords cracked, if it comes at the cost of my machine break down and a lot of time being spent, I can conclude that it may not be worth it to do a brute force attack. Instead, I would look for alternatives to attack using a dictionary attack. The following are the possible dictionaries used to crack the passwords:
- https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10-million-password-list-top-1000000.txt
- https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/100k-most-used-passwords-NCSC.txt
Others can be found here:
https://github.com/danielmiessler/SecLists/tree/master/Passwords/Common-Credentials
`hashcat64 -D 2 -m 0 -o most100k-moderate-hash-cracked.csv cleaned-hash-moderate.txt 100k-most-used-passwords-NCSC.txt -O --potfile-disable`

`hashcat64 -D 2 -m 0 -o most10m-moderate-hash-cracked.csv cleaned-hash-moderate.txt 10-million-password-list-top-1000000.txt -O --potfile-disable`

As seen by the abysmal performance, using filtered alternative word text doesn't help in cracking more passwords.
### 8.3
A dictionary attack is also run on the strong password set. This is done to prove that commonly used passwords are majorly found in the weak password sets.
`hashcat64 -D 2 -m 0 -o strong-hash-cracked.csv cleaned-hash-strong.txt rockyou.txt -O --potfile-disable`

As seen no strong passwords are found in the rockyou.txt
As it takes too much time to do a brute force attack on the strong passwords, I have elected to create a rainbow table with the following parameters instead, which hopefully would help in cracking the password:

As seen from the result, the has table doesnt help as there is 0 of 67 passwords cracked.
After combining the csv files and removing the duplicates, the following `competition.csv` is obtained:
```csvpreview
Hash, Password
4297f44b13955235245b2497399d7a93,123123
5ebe2294ecd0e0f08eab7690d2a6ee69,secret
1660fe5c81c4ce64a2611494c439e1ba,jennifer
8621ffdbc5698829397d97767ac13db3,dragon
fe01d67a002dfa0f3ac084298142eccd,orange
72b302bf297a228a75730123efef7c41,banana
6eea9b7ef19179a06954edd0f6c05ceb,qwertyuiop
0d107d09f5bbe40cade3de5c71e9e9b7,letmein
f30aa7a662c728b7407c54ae6bfd27d1,hello123
23ec24c5ca59000543cee1dfded0cbea,sheep
482c811da5d5b4bc6d497ffa98491e38,password123
84df077bcb1bd39ab1a3294de0cf655b,skeleton
912ec803b2ce49e4a541068d495ab570,asdf
e10adc3949ba59abbe56e057f20f883e,123456
827ccb0eea8a706c4c34a16891f84e7b,12345
5f4dcc3b5aa765d61d8327deb882cf99,password
e99a18c428cb38d5f260853678922e03,abc123
d8578edf8458ce06fbc5bb76a58c5ca4,qwerty
7c6a180b36896a0a8c02787eeafb0e4c,password1
58b4e38f66bcdb546380845d6af27187,qwerty1234
c822c1b63853ed273b89687ac505f9fa,google
26cae7718c32180a7a0f8e19d6d40a59,facebook
79464212afb7fd6c38699d0617eaedeb,television
9443b0fceb8c03b6a514a706ea69df0b,donkey
ab56b4d92b40713acc5af89985d4b786,abcde
7d9ad0211d6493e8d55a4a75de3f90a1,nintendo
82210e61e8f415525262575b20fae48d,treasure
2ab96390c7dbe3439de74d0c9b0b1767,hunter2
c37bf859faf392800d739a41fe5af151,98765
b497dd1a701a33026f7211533620780d,drowssap
8632c375e9eba096df51844a5a43ae93,security1
0832c1202da8d382318e329a7c133ea0,cats
efa88170397c87c264cb471f3cf86e6d,let me in
41fb027d1c23536f9e0b2dde019e1a37,2011992
6ee20298b4f899d0af9f7f04b342f332,howareyou1
6cd1e21cf8e989f9fc662c40b0d80294,4ever&ever
981d304c3f23f463adfefc42028f7f0c,zyx987
342f5c77ed008542e78094607ce1f7f3,firstname
a5c9b509c134142fbfe0276c22ffa4be,genghis
7f59a125a3f57ff02c3691b7a829b837,cuvant
4e7e00ce3e827e48d68f3309dabbb1db,mycatiscute
f46565ba900fb8fb166521bd4bb6e2e7,Cara123
dd7ee3bfea94a9af1d0495e3e2efd339,yellowfellow
417432b93db6d7654c9612c2cc37dedd,fidoismydog
c3cb0e6b73ed4ea1103257ac539a015a,bubbles1980
b419e81184e9492a74d9646e23ebc82d,kamikaze123
cf9335aa407b76812d6fe661de163756,3.1415926535
8b2d6d8c68b069ea1c27308cd4b3d3ac,happydays219
3805248410673a8be6aa4807e61fb5ae,poiuytrewq
1897a69ef451f0991bb85c6e7c35aa31,1a2b3c4d
0239181e4d9c76a12e60675013ff4376,dogsandcats
318e2407a82f72b01d61ab1c52a719b2,12secret123
ac33c7d806f98d4e3f91240c6de301fd,superduper456
ade19e33351c5ef7923b0fcecac5efe8,jac0b!
1e46805eff2b78c440e88cd8d4cce788,b1umua
9ded7fab83986b60bdbb4a75dab019bb,AcH98
1897a69ef451f0991bb85c6e7c35aa31,1a2b3c4d
8a8ed1d1160152f7656f5e823a8bdffa,tr0mb0n3
700055094032bf4942fa3f670d5330f5,SMArq123
039624cfe1e84d7e717bd92767a8de84,snowball12
e397c0aef7d112929a4b9ffcf97175e8,5987ty3523
d1fdfebfd5a4fa30644a40c042856e87,$eLLer
b9cba343ab1df65293bcd23e668c7179,ekazPS26
```
In total, 63 of 201 hashes were cracked, all of which coming moderate and weak hashes. None of the strong hashes were cracked given that there is little time for this assignment and burning up my gpu to crack the hashes may result in future problems.
To conclude, if we already know some factors of the passwords (i.e length, characters set), using hashcat rule-based attack can also be efficient as demonstrated in Part 7 to crack the salted password. However, if there are too many unknowns (i.e length of passwords, characters set), a lot of time will be spent trying out different combinations and length with different rules. It is going to take a tremendous amount of time and effort to crack the passwords this way without dictionaries.
FYI (https://www.computer.org/publications/tech-news/trends/why-you-should-prolong-gpu-lifespan/)