1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
#!/bin/ruby characters_to_use="ABCD" minimum_password_length=5 maximum_password_length=15 def password_correct?(test_password) # This is just for testing. There's no need to alter this bit! return true if test_password == "DCBAAAA" return false end char_array=characters_to_use.split(//) is_access_granted = false (minimum_password_length..maximum_password_length).each do |password_length| # loop to gradually increase password length combinations=char_array.length**password_length (0..combinations-1).each do |perm_number| # loop through combinations for a given length password="" (1..password_length).each do |char_number| # loop through characters char_reference = (perm_number / char_array.length**(char_number-1)).floor % char_array.length character = char_array[char_reference] password << character end if password_correct?(password) puts "#{password} | Access Denied | #{perm_number} / #{combinations}" else puts "#{password} | Access Granted" is_access_granted = true break end end break if is_access_granted end
Refactorings
No refactoring yet !
Ishkur
October 11, 2008, October 11, 2008 02:36, permalink
Hmmm, I certainly can't speak for everyone here (nor do I care to, for that matter), but um... wtfm8?
I just don't see that as the greatest idea. And also, why Ruby?
John Sietsma
October 13, 2008, October 13, 2008 11:07, permalink
Here is a first pass refactor.
A few quick notes:
* password check was the wrong way around.
* no need for the returns in password_correct?
* use the range object rather then two separate min and max variables.
I'm suss on anything that is nested more then once and functions that go on too long. Breaking out the loops into functions lets you remove the is_access_granted flag and the multiple breaks. I think it makes it more readable, but you may disagree! It allows you to give a functions reasonable names and users don't have to grok the code.
I think there still some way to go in refactoring this code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
#!/bin/ruby class Cracker def initialize(char_array, password_range) @char_array = char_array @password_range = password_range end def password_correct?(test_password) test_password == "DCBAAAA" end def generate_password( perm_number, password_length ) password="" (1..password_length).each do |char_number| # loop through characters char_reference = (perm_number / @char_array.length**(char_number-1)).floor % @char_array.length character = @char_array[char_reference] password << character end password end def do_combination( num_combinations, password_length ) (0..num_combinations-1).each do |perm_number| # loop through combinations for a given length password = generate_password( perm_number, password_length ) return password, perm_number if password_correct?(password) end end def crack() (@password_range).each do |password_length| # loop to gradually increase password length num_combinations=@char_array.length**password_length password, perm_number = do_combination(num_combinations, password_length) if password puts "#{password} | Access Granted | #{perm_number} / #{num_combinations}" return password end end end end cracker = Cracker.new( "ABCD".split(//), (5..15) ) password = cracker.crack()
John Sietsma
October 14, 2008, October 14, 2008 01:20, permalink
*EDIT* Removed smart-arse comments about trolls.
In this refactor I've tried to flatten the nested loops and bring the important code back to the caller. The closures and co-routines of Ruby (and other languages) make this easy without excessive memory usage.
I'm curious to know whether people think this sort refactoring is worthwhile? I'm not entirely sure if it's worth the extra code. Perhaps just a yield in the original code would bring the code to the caller without the extra bloat from flattening the nestings.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
#!/bin/ruby class Cracker def initialize(char_array, password_range) @char_array = char_array @password_range = password_range end def make_password( perm_number, password_length ) password = (1..password_length).map do |char_number| char_reference = (perm_number / @char_array.length**(char_number-1)).floor % @char_array.length @char_array[char_reference] end password.to_s end def password_combinations( password_length ) num_combinations = @char_array.length**password_length (0..num_combinations-1).map do |perm_number| yield make_password( perm_number, password_length ) end end def all_passwords() @password_range.map do |password_length| password_combinations(password_length) do |password| yield password; end end end def crack() all_passwords() do |password| return password if yield password end return nil end end #------------------------------------- # Caller code def password_correct?(test_password) test_password == "DCBAAAA" end cracker = Cracker.new( "ABCD".split(//), (5..15) ) password = cracker.crack() do |test_password| password_correct? test_password end if password puts "#{password} | Access Granted" end
Adam
October 14, 2008, October 14, 2008 04:50, permalink
Array#combination requires Ruby 1.9. If you re-implement it, it should otherwise work with earlier Ruby versions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
require 'set' module Cracker class Password attr_reader :characters, :range def initialize(characters, range) @characters, @range = characters.split(''), Array(range) end def each(&block) range.each { |length| Combination.new(characters, length).each(&block) } end end class Combination < Struct.new(:characters, :length) def characters super * length end def each(&block) combinations = Set.new characters.combination(length).each do |combination| yield(combination.join) unless combinations.include?(combination) combinations << combination end end end end Cracker::Password.new("ABCD", 5..15).each do |password| password_correct?(password) end
Just a quick-and-dirty for a brute force attack. I made this to break into my router, because I wanted to check the settings (so a hard system restore wouldn't work because it would wipe them). I've included a little test method instead of the actual login script and reduced the selection of characters (characters_to_use) to help with speed of testing. I'd be interested to see what people come up with.