#!/usr/bin/ruby # Author: SkyOut # Date: June 2008 # Website: http://wired-security.net/ # This little helper script compares two directory with each # other recursively. If two files are the same (based on their # md5 checksum) this gets echoed to the user (if "v" is set) and # written to a output file (result.txt). # How can this help? # Imagine: You have found a vulnerability in a software of a hardware # utility, such as a router (so: firmware) and you want to know if this # part (for example a *.cgi script) is used in other firmwares as well, but # might differ in the name or location. Then this helper utility comes in: # Just download both firmwares and all scripts and check them against each # other. If something is totally equal you will get to know it and can confirm # the bug in more then only one firmware... Handy, isn't it? Sometimes yes! # The required modules we will use later # 'find' gets used for directory indexing # 'digest/md5' gets used for checksum calculation require 'find' require 'digest/md5' # Print the usage dialogue and quit the program def usage() puts "" puts "ruby(.exe) dir2md5.rb [directory] [directory] [v]" exit(0) end # Make sure at least two arguments have been set if (ARGV[0] == nil) || (ARGV[1] == nil) usage() end # Write the arguments given by the user to local variables (dir1, dir2, verbose (optional)) dir1 = ARGV[0] dir2 = ARGV[1] verbose = ARGV[2] # Create the file for the first directory to be indexed dir1file = File.open("#{dir1}/../dir1file.txt", File::WRONLY|File::CREAT|File::TRUNC, 0777) # Create the file for the second directory to be indexed dir2file = File.open("#{dir2}/../dir2file.txt", File::WRONLY|File::CREAT|File::TRUNC, 0777) # Create the file, that will hold our result later on outfile = File.open("result.txt", File::WRONLY|File::CREAT|File::TRUNC, 0777) puts "" puts "Indexing directory #{dir1}..." puts "" # Jump over . and .. excludes = [".", ".."] # Go through the first directory Find.find(dir1) do |path| # Test if it is a directory if FileTest.directory?(path) # If the directory equals . or .. don't go deeper into it if excludes.include?(File.basename(path)) Find.prune else # Go on... next end else # Calculate a checksum with md5 of the file found digest = Digest::MD5.hexdigest(File.read("#{path}")) if (verbose == "v") puts "#{path}:#{digest}" end # Write it to the indexing file of the first directory (dir1file.txt) dir1file.puts "#{path} #{digest}" end end # Close the file handle dir1file.close puts "" puts "Indexing directory #{dir2}..." puts "" # Jump over . and .. excludes = [".", ".."] # Go through the second directory Find.find(dir2) do |path| # Test if it is a directory if FileTest.directory?(path) # If the directory equals . or .. don't go deeper into it if excludes.include?(File.basename(path)) Find.prune else # Go on... next end else # Calculate a checksum with md5 of the file found digest = Digest::MD5.hexdigest(File.read("#{path}")) if (verbose == "v") puts "#{path}:#{digest}" end # Write it to the indexing file of the second directory (dir2file.txt) dir2file.puts "#{path} #{digest}" end end # Close the file handle dir2file.close puts "" puts "Comparing #{dir1} with #{dir2}..." puts "" # Open the first indexing file dir1file2 = File.open("#{dir1}/../dir1file.txt", File::RDONLY) begin # Go through the file line by line while (line = dir1file2.readline) # Get the filename fp = line fp = fp.split(" ") fp = fp.fetch(0) # Get the md5 hash value hash = line hash = hash.split(" ") hash = hash.fetch(1) # Open the second indexing file dir2file2 = File.open("#{dir2}/../dir2file.txt", File::RDONLY) begin # Go through the file line by line while (line2 = dir2file2.readline) # Get the filename fp2 = line2 fp2 = fp2.split(" ") fp2 = fp2.fetch(0) # Get the md5 hash value hash2 = line2 hash2 = hash2.split(" ") hash2 = hash2.fetch(1) # Check if the hash from dir1file equals the one of dir2file if (hash == hash2) if (verbose == "v") puts "" puts "#{fp} == #{fp2}" end # Write it to the resulting file! outfile.puts "#{fp} == #{fp2}" else if (verbose == "v") puts "Comparing #{fp}:#{hash} to #{fp2}:#{hash2}" end next end end # End of file reached, close it... rescue EOFError dir2file2.close end end # End of file reached, close it... rescue EOFError dir1file2.close end puts "" puts "Finished... Result written to - result.txt -..." puts "" # JOB DONE! exit(0)