2011-07-09 02:14:24 +00:00
|
|
|
require 'open3'
|
2011-11-09 16:44:07 +00:00
|
|
|
require 'fileutils'
|
2011-07-09 02:14:24 +00:00
|
|
|
require 'rex/proto/ntlm/crypt'
|
|
|
|
|
2011-11-09 16:44:07 +00:00
|
|
|
|
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
module Msf
|
|
|
|
|
|
|
|
###
|
|
|
|
#
|
|
|
|
# This module provides methods for working with John the Ripper
|
|
|
|
#
|
|
|
|
###
|
|
|
|
module Auxiliary::JohnTheRipper
|
|
|
|
include Msf::Auxiliary::Report
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
#
|
|
|
|
# Initializes an instance of an auxiliary module that calls out to John the Ripper (jtr)
|
|
|
|
#
|
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
OptPath.new('JOHN_BASE', [false, 'The directory containing John the Ripper (src, run, doc)']),
|
|
|
|
OptPath.new('JOHN_PATH', [false, 'The absolute path to the John the Ripper executable'])
|
|
|
|
], Msf::Auxiliary::JohnTheRipper
|
|
|
|
)
|
|
|
|
|
|
|
|
@run_path = nil
|
|
|
|
@john_path = ::File.join(Msf::Config.install_root, "data", "john")
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
autodetect_platform
|
2011-07-31 19:46:36 +00:00
|
|
|
end
|
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
def autodetect_platform
|
|
|
|
cpuinfo_base = ::File.join(Msf::Config.install_root, "data", "cpuinfo")
|
2011-11-09 16:44:07 +00:00
|
|
|
return @run_path if @run_path
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
case ::RUBY_PLATFORM
|
|
|
|
when /mingw|cygwin|mswin/
|
2011-11-09 16:44:07 +00:00
|
|
|
data = `"#{cpuinfo_base}/cpuinfo.exe"` rescue nil
|
2011-07-09 02:14:24 +00:00
|
|
|
case data
|
|
|
|
when /sse2/
|
2011-11-09 16:44:07 +00:00
|
|
|
@run_path ||= "run.win32.sse2/john.exe"
|
2011-07-09 02:14:24 +00:00
|
|
|
when /mmx/
|
2011-11-09 16:44:07 +00:00
|
|
|
@run_path ||= "run.win32.mmx/john.exe"
|
2011-07-09 02:14:24 +00:00
|
|
|
else
|
2011-11-09 16:44:07 +00:00
|
|
|
@run_path ||= "run.win32.any/john.exe"
|
2011-07-09 02:14:24 +00:00
|
|
|
end
|
2011-11-20 01:05:14 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
when /x86_64-linux/
|
2011-11-09 16:44:07 +00:00
|
|
|
::FileUtils.chmod(755, "#{cpuinfo_base}/cpuinfo.ia64.bin") rescue nil
|
|
|
|
data = `#{cpuinfo_base}/cpuinfo.ia64.bin` rescue nil
|
2011-07-09 02:14:24 +00:00
|
|
|
case data
|
|
|
|
when /mmx/
|
2011-11-09 16:44:07 +00:00
|
|
|
@run_path ||= "run.linux.x64.mmx/john"
|
2011-07-09 02:14:24 +00:00
|
|
|
else
|
2011-11-09 16:44:07 +00:00
|
|
|
@run_path ||= "run.linux.x86.any/john"
|
2011-07-31 19:46:36 +00:00
|
|
|
end
|
2011-11-20 01:05:14 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
when /i[\d]86-linux/
|
2011-11-20 01:05:14 +00:00
|
|
|
::FileUtils.chmod(755, "#{cpuinfo_base}/cpuinfo.ia32.bin") rescue nil
|
2011-11-09 16:44:07 +00:00
|
|
|
data = `#{cpuinfo_base}/cpuinfo.ia32.bin` rescue nil
|
2011-07-09 02:14:24 +00:00
|
|
|
case data
|
|
|
|
when /sse2/
|
2011-11-09 16:44:07 +00:00
|
|
|
@run_path ||= "run.linux.x86.sse2/john"
|
2011-07-09 02:14:24 +00:00
|
|
|
when /mmx/
|
2011-11-09 16:44:07 +00:00
|
|
|
@run_path ||= "run.linux.x86.mmx/john"
|
2011-07-09 02:14:24 +00:00
|
|
|
else
|
2011-11-09 16:44:07 +00:00
|
|
|
@run_path ||= "run.linux.x86.any/john"
|
2011-07-31 19:46:36 +00:00
|
|
|
end
|
|
|
|
end
|
2011-11-09 16:44:07 +00:00
|
|
|
@run_path
|
2011-07-09 02:14:24 +00:00
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
def john_session_id
|
|
|
|
@session_id ||= ::Rex::Text.rand_text_alphanumeric(8)
|
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-11-11 18:09:42 +00:00
|
|
|
def john_pot_file
|
|
|
|
::File.join( ::Msf::Config.config_directory, "john.pot" )
|
|
|
|
end
|
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
def john_cracked_passwords
|
|
|
|
ret = {}
|
2011-11-11 18:09:42 +00:00
|
|
|
return ret if not ::File.exist?(john_pot_file)
|
|
|
|
::File.open(john_pot_file, "rb") do |fd|
|
2011-07-09 02:14:24 +00:00
|
|
|
fd.each_line do |line|
|
|
|
|
hash,clear = line.sub(/\r?\n$/, '').split(",", 2)
|
2011-07-31 19:46:36 +00:00
|
|
|
ret[hash] = clear
|
2011-07-09 02:14:24 +00:00
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
end
|
2011-07-09 02:14:24 +00:00
|
|
|
ret
|
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
def john_show_passwords(hfile, format=nil)
|
|
|
|
res = {:cracked => 0, :uncracked => 0, :users => {} }
|
2011-11-11 18:09:42 +00:00
|
|
|
|
|
|
|
pot = john_pot_file
|
|
|
|
conf = ::File.join(john_base_path, "confs", "john.conf")
|
|
|
|
|
|
|
|
cmd = [ john_binary_path, "--show", "--conf=#{conf}", "--pot=#{pot}", hfile]
|
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
if format
|
|
|
|
cmd << "--format=" + format
|
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-11 01:07:46 +00:00
|
|
|
if RUBY_VERSION =~ /^1\.8\./
|
|
|
|
cmd = cmd.join(" ")
|
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
::IO.popen(cmd, "rb") do |fd|
|
|
|
|
fd.each_line do |line|
|
2011-10-16 18:59:27 +00:00
|
|
|
print_status(line)
|
|
|
|
if line =~ /(\d+) password hash(es)* cracked, (\d+) left/m
|
2011-07-31 19:46:36 +00:00
|
|
|
res[:cracked] = $1.to_i
|
2011-07-09 02:14:24 +00:00
|
|
|
res[:uncracked] = $2.to_i
|
2011-07-31 19:46:36 +00:00
|
|
|
end
|
2011-07-09 02:14:24 +00:00
|
|
|
|
|
|
|
bits = line.split(':')
|
|
|
|
next if not bits[2]
|
2011-10-16 18:59:27 +00:00
|
|
|
if (format== 'lm' or format == 'nt')
|
|
|
|
res[ :users ][ bits[0] ] = bits[1]
|
|
|
|
else
|
|
|
|
bits.last.chomp!
|
|
|
|
res[ :users ][ bits[0] ] = bits.drop(1)
|
|
|
|
end
|
2011-11-20 01:05:14 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
res
|
|
|
|
end
|
2011-11-20 01:05:14 +00:00
|
|
|
|
2011-10-16 18:59:27 +00:00
|
|
|
def john_unshadow(passwd_file,shadow_file)
|
2011-11-20 01:05:14 +00:00
|
|
|
|
2011-10-16 18:59:27 +00:00
|
|
|
retval=""
|
2011-11-20 01:05:14 +00:00
|
|
|
|
2011-10-16 18:59:27 +00:00
|
|
|
if File.exists?(passwd_file)
|
|
|
|
unless File.readable?(passwd_file)
|
|
|
|
print_error("We do not have permission to read #{passwd_file}")
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
else
|
|
|
|
print_error("File does not exist: #{passwd_file}")
|
|
|
|
return nil
|
|
|
|
end
|
2011-11-20 01:05:14 +00:00
|
|
|
|
2011-10-16 18:59:27 +00:00
|
|
|
if File.exists?(shadow_file)
|
|
|
|
unless File.readable?(shadow_file)
|
|
|
|
print_error("We do not have permission to read #{shadow_file}")
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
else
|
|
|
|
print_error("File does not exist: #{shadow_file}")
|
|
|
|
return nil
|
|
|
|
end
|
2011-11-20 01:05:14 +00:00
|
|
|
|
|
|
|
|
2011-10-16 18:59:27 +00:00
|
|
|
cmd = [ john_binary_path.gsub(/john$/, "unshadow"), passwd_file , shadow_file ]
|
2011-11-20 01:05:14 +00:00
|
|
|
|
2011-10-16 18:59:27 +00:00
|
|
|
if RUBY_VERSION =~ /^1\.8\./
|
|
|
|
cmd = cmd.join(" ")
|
|
|
|
end
|
|
|
|
::IO.popen(cmd, "rb") do |fd|
|
|
|
|
fd.each_line do |line|
|
|
|
|
retval << line
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return retval
|
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
def john_wordlist_path
|
|
|
|
::File.join(john_base_path, "wordlists", "password.lst")
|
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
def john_binary_path
|
|
|
|
if datastore['JOHN_PATH'] and ::File.file?(datastore['JOHN_PATH'])
|
2011-11-09 16:44:07 +00:00
|
|
|
path = datastore['JOHN_PATH']
|
|
|
|
::FileUtils.chmod(755, path) rescue nil
|
2011-11-20 01:05:14 +00:00
|
|
|
path
|
2011-07-09 02:14:24 +00:00
|
|
|
end
|
2011-11-20 01:05:14 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
if not @run_path
|
|
|
|
if ::RUBY_PLATFORM =~ /mingw|cygwin|mswin/
|
|
|
|
::File.join(john_base_path, "john.exe")
|
|
|
|
else
|
2011-11-09 16:44:07 +00:00
|
|
|
path = ::File.join(john_base_path, "john")
|
|
|
|
::FileUtils.chmod(755, path) rescue nil
|
2011-11-20 01:05:14 +00:00
|
|
|
path
|
2011-07-09 02:14:24 +00:00
|
|
|
end
|
|
|
|
else
|
2011-11-09 16:44:07 +00:00
|
|
|
path = ::File.join(john_base_path, @run_path)
|
|
|
|
::FileUtils.chmod(755, path) rescue nil
|
|
|
|
path
|
2011-07-09 02:14:24 +00:00
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
end
|
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
def john_base_path
|
|
|
|
if datastore['JOHN_BASE'] and ::File.directory?(datastore['JOHN_BASE'])
|
|
|
|
return datastore['JOHN_BASE']
|
|
|
|
end
|
|
|
|
if datastore['JOHN_PATH'] and ::File.file?(datastore['JOHN_PATH'])
|
|
|
|
return ::File.dirname( datastore['JOHN_PATH'] )
|
|
|
|
end
|
|
|
|
@john_path
|
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
def john_expand_word(str)
|
|
|
|
res = [str]
|
|
|
|
str.split(/\W+/) {|w| res << w }
|
|
|
|
res.uniq
|
2011-07-31 19:46:36 +00:00
|
|
|
end
|
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
def john_lm_upper_to_ntlm(pwd, hash)
|
2011-07-31 19:46:36 +00:00
|
|
|
pwd = pwd.upcase
|
2011-07-09 02:14:24 +00:00
|
|
|
hash = hash.upcase
|
2011-10-15 04:35:06 +00:00
|
|
|
Rex::Text.permute_case(pwd).each do |str|
|
2011-07-09 02:14:24 +00:00
|
|
|
if hash == Rex::Proto::NTLM::Crypt.ntlm_hash(str).unpack("H*")[0].upcase
|
|
|
|
return str
|
|
|
|
end
|
|
|
|
end
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
def john_crack(hfile, opts={})
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
res = {:cracked => 0, :uncracked => 0, :users => {} }
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-11-11 18:09:42 +00:00
|
|
|
# Don't bother making a log file, we'd just have to rm it when we're
|
|
|
|
# done anyway.
|
|
|
|
cmd = [ john_binary_path, "--session=" + john_session_id, "--nolog"]
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
if opts[:conf]
|
|
|
|
cmd << ( "--conf=" + opts[:conf] )
|
|
|
|
else
|
|
|
|
cmd << ( "--conf=" + ::File.join(john_base_path, "confs", "john.conf") )
|
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-11-11 18:09:42 +00:00
|
|
|
if opts[:pot]
|
|
|
|
cmd << ( "--pot=" + opts[:pot] )
|
|
|
|
else
|
|
|
|
cmd << ( "--pot=" + john_pot_file )
|
|
|
|
end
|
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
if opts[:format]
|
|
|
|
cmd << ( "--format=" + opts[:format] )
|
|
|
|
end
|
|
|
|
|
|
|
|
if opts[:wordlist]
|
|
|
|
cmd << ( "--wordlist=" + opts[:wordlist] )
|
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
if opts[:incremental]
|
|
|
|
cmd << ( "--incremental=" + opts[:incremental] )
|
2011-07-31 19:46:36 +00:00
|
|
|
end
|
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
if opts[:single]
|
|
|
|
cmd << ( "--single=" + opts[:single] )
|
2011-07-31 19:46:36 +00:00
|
|
|
end
|
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
if opts[:rules]
|
|
|
|
cmd << ( "--rules=" + opts[:rules] )
|
2011-07-31 19:46:36 +00:00
|
|
|
end
|
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
cmd << hfile
|
|
|
|
|
2011-07-11 01:07:46 +00:00
|
|
|
if RUBY_VERSION =~ /^1\.8\./
|
|
|
|
cmd = cmd.join(" ")
|
|
|
|
end
|
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
::IO.popen(cmd, "rb") do |fd|
|
|
|
|
fd.each_line do |line|
|
|
|
|
print_status("Output: #{line.strip}")
|
|
|
|
end
|
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
|
2011-07-09 02:14:24 +00:00
|
|
|
res
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2011-07-31 19:46:36 +00:00
|
|
|
|