├── util └── cms_lister │ └── list.txt ├── README.md ├── md5_lookup.rb └── cms_lister.rb /util/cms_lister/list.txt: -------------------------------------------------------------------------------- 1 | readme.txt 2 | readme.html 3 | readme.php 4 | robots.txt 5 | .htaccess 6 | changelog.txt 7 | license.txt 8 | install.txt 9 | help.php 10 | wp-admin/ 11 | administrator/ 12 | administration/ 13 | wp-login.php 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | + Metasploit modules:
2 | + md5_lookup.rb - reverse provided MD5 hashes by lookup in online databases 3 | + cms_lister.rb - list remote directory by comparision with local directory (or paths list), useful i.e. for CMS recon
4 | + util - samples and additional files for appropriate modules 5 | + cms_lister 6 | + list.txt - sample list of common paths for cms_lister 7 | 8 | md_lookup.rb 9 | - 10 | /*
11 | this module has been ported as a part of metasploit-framework:
12 | https://github.com/rapid7/metasploit-framework/blob/master/tools/password/md5_lookup.rb 13 |
- thanks to sinn3r: [![Social](https://img.shields.io/twitter/follow/_sinn3r.svg?style=social)](http://www.twitter.com/_sinn3r)
14 | */
15 | ![](https://pbs.twimg.com/media/B4xl9HQIUAAfwr0.png) 16 | cms_lister.rb 17 | - 18 | options:
19 | ![](http://hasherezade.net/misc/pics/cms_lister_1.png) 20 | 21 | running:
22 | ![](http://hasherezade.net/misc/pics/cms_lister_2.png) 23 | -------------------------------------------------------------------------------- /md5_lookup.rb: -------------------------------------------------------------------------------- 1 | # 2 | # This module requires Metasploit: http//metasploit.com/download 3 | # Current source: https://github.com/rapid7/metasploit-framework 4 | # 5 | # MD5 lookup 6 | # 7 | # Author: hasherezade (http://hasherezade.net) 8 | # Source: https://github.com/hasherezade/metasploit_modules/blob/master/md5_lookup.rb 9 | # 10 | 11 | require 'msf/core' 12 | 13 | class Metasploit3 < Msf::Auxiliary 14 | include Msf::Exploit::Remote::HttpClient 15 | 16 | def initialize(info={}) 17 | super(update_info(info, 18 | 'Name' => "Md5 lookup", 19 | 'Description' => %q{ 20 | This auxiliary module attempts to reverse provided MD5 hashes by lookup in online databases. 21 | }, 22 | 'License' => MSF_LICENSE, 23 | 'Author' => 24 | [ 25 | 'hAsh (http://hasherezade.net)' # Metasploit module 26 | ] 27 | )) 28 | 29 | register_options( 30 | [ 31 | OptPath.new('PATH', [true, 'File with md5 hashes']), 32 | ]) 33 | register_advanced_options( 34 | [ 35 | OptPort.new('RPORT',[true, 'Port of the lookup service', 80]), 36 | OptString.new('RHOST',[true, 'Host used for lookup', 'md5cracker.org']), 37 | OptString.new('TARGETURI', [true, 'URI of the lookup service', '/api/api.cracker.php']), 38 | OptString.new('DATABASES',[true, 'Comma separated list of MD5 online databases', 39 | "authsecu, i337.net, md5.my-addr.com, md5.net, md5crack, md5cracker.org, md5decryption.com, md5online.net, md5pass, netmd5crack, tmto" 40 | ]), 41 | ]) 42 | deregister_options('VHOST', 'Proxies') 43 | end 44 | 45 | # utils 46 | def read_file() 47 | path = datastore['PATH'] 48 | print_status("Trying to read file: #{path}") 49 | 50 | if not File::exist?(path) 51 | print_error("No such file") 52 | return nil 53 | elsif not File::file?(path) 54 | print_error("Invalid path") 55 | return nil 56 | end 57 | hash_counter = 0 58 | hashes = Set.new 59 | my_file = File::new(path, mode="r") 60 | my_file.each {|line| 61 | hash = fetchMd5(line) 62 | if hash 63 | hashes << hash 64 | hash_counter += 1 65 | end 66 | } 67 | my_file.close 68 | print_status("Found hashes: #{hash_counter}, unique: #{hashes.length}") 69 | return hashes 70 | end 71 | 72 | def get_string_between(my_string, start_at, end_at) 73 | my_string = "#{my_string}" 74 | 75 | ini = my_string.index(start_at) 76 | return nil if ini == 0 77 | 78 | ini += start_at.length 79 | length = my_string.index(end_at, ini).to_i - ini 80 | my_string[ini,length] 81 | end 82 | 83 | def fetchMd5(my_string) 84 | if my_string =~ /([0-9a-fA-F]{32})/ 85 | return $1 86 | end 87 | return nil 88 | end 89 | 90 | def md5search(hash, database) 91 | hash = fetchMd5(hash) 92 | if not hash 93 | return nil 94 | end 95 | 96 | port = datastore['RPORT'] 97 | old_ssl = autoenable_ssl(port) 98 | res = send_request_cgi({ 99 | 'method' => 'GET', 100 | 'uri' => normalize_uri(target_uri.path), 101 | 'vars_get' => { "database" => database, "hash" => hash } 102 | }) 103 | datastore['SSL'] = old_ssl 104 | 105 | if not res or res.code != 200 or res.body.empty? 106 | print_error("#{url}, db: #{database} - returned invalid response") 107 | return nil 108 | end 109 | # "status":true, "result":"123", 110 | if res.body =~ /\>404 Not Found\ "CMS File Lister", 21 | 'Description' => %q{ 22 | This auxiliary module attempts to list remote directory by comparision with local directory (or paths list) 23 | }, 24 | 'License' => MSF_LICENSE, 25 | 'Author' => 26 | [ 27 | 'hAsh (http://hasherezade.net)' # Metasploit module 28 | ] 29 | )) 30 | 31 | register_options( 32 | [ 33 | OptPath.new('CMS_DIR',[false, 'Directory with CMS']), 34 | OptPath.new('PATH', [false, 'File with list of files to search']), 35 | OptString.new('TARGETURI', [true, 'URI of the lookup service', '/']), 36 | OptRegexp.new('NOT_FOUND', [true, '404 pattern', '/Not Found/']), 37 | OptInt.new('REDIR_DEPTH', [true, "Redirection depth, max = #{MAX_REDIR}", 2]), 38 | OptRegexp.new('EXCLUDE_NAMES', [true, 'Skip files with names matching the pattern','\.(?:php|js|css|png|jpg|gif)$']), 39 | OptBool.new('DISPLAY_ERR', [true, 'Display error codes (if different than 404)', 'false']), 40 | OptBool.new('COMPARE_FILES', [true, "Compare remote file with local (Out: [+] - same; [-] - different; [?] - not compared)",'true']) 41 | ]) 42 | deregister_options('VHOST', 'Proxies') 43 | end 44 | 45 | # utils 46 | def read_dir(dir) 47 | return nil if not dir or dir.length == 0 48 | 49 | if not File::directory?(dir) 50 | print_error("Not a directory: #{dir}") 51 | return nil 52 | end 53 | 54 | files = Set.new 55 | 56 | Dir[ File.join(dir, '**', '*') ].reject { |p| File.directory? p 57 | full_path = "#{p}" 58 | dir_path = "#{dir}" 59 | suffix = full_path[dir_path.length, full_path.length - dir_path.length] 60 | if File::directory?(full_path) 61 | suffix += '/' 62 | end 63 | files << suffix 64 | } 65 | return files 66 | end 67 | 68 | def read_file(path) 69 | if not File::exist?(path) 70 | print_error("No such file: #{path}") 71 | return nil 72 | elsif not File::file?(path) 73 | print_error("Invalid path: #{path}") 74 | return nil 75 | end 76 | 77 | paths = Set.new 78 | my_file = File::new(path, mode="r") 79 | my_file.each {|line| 80 | line = line.strip 81 | if line 82 | paths << line 83 | end 84 | } 85 | my_file.close 86 | print_status("Loaded #{paths.length} paths") if datastore['VERBOSE'] 87 | return paths 88 | end 89 | 90 | def get_url(path, is_ssl) 91 | proto = "http" 92 | proto = "https" if is_ssl 93 | 94 | url = "#{proto}:/" + normalize_uri("#{datastore['RHOST']}//#{datastore['TARGETURI']}//#{path}") 95 | end 96 | 97 | def compare_content(path, res_body) 98 | dir = datastore['CMS_DIR'] 99 | return -1 if not dir 100 | 101 | path = "#{dir}/#{path}" 102 | 103 | if not File::exist?(path) 104 | return -1 105 | elsif File::directory?(path) 106 | return 1 107 | elsif not File::file?(path) 108 | return -1 109 | end 110 | contents = File.read(path) 111 | return 1 if res_body == contents 112 | return 0 113 | end 114 | 115 | def cmpres_to_str(is_same) 116 | verified = '[?]' 117 | if is_same == 1 118 | verified = '[+]' 119 | elsif is_same == 0 120 | verified = '[-]' 121 | end 122 | return verified 123 | end 124 | 125 | def path_search(path, url, port, redir_depth = 0) 126 | return nil if not url 127 | 128 | print_status("GET: #{url}") if (datastore['VERBOSE']) 129 | autoenable_ssl(port) 130 | res = send_request_cgi({ 131 | 'method' => 'GET', 132 | 'uri' => url, 133 | 'rhost' => datastore['RHOST'], 134 | 'rport' => port 135 | }) 136 | 137 | if not res 138 | print_error("#{url}") 139 | return false 140 | end 141 | 142 | if res.code == 404 or res.body =~ datastore['NOT FOUND'] 143 | print_error("Not found: #{res.code}") if datastore['VERBOSE'] and datastore['DISPLAY_ERR'] 144 | return false 145 | end 146 | 147 | if res.code == 200 148 | is_same = -1 149 | if datastore['COMPARE_FILES'] 150 | is_same = compare_content(path, res.body) 151 | end 152 | 153 | verified = cmpres_to_str(is_same) 154 | print_good("#{verified} #{url}") 155 | return true 156 | end 157 | 158 | if (res.code == 301 or res.code == 302) and res.redirection 159 | if (redir_depth < MAX_REDIR and redir_depth < datastore['REDIR_DEPTH']) 160 | redir_uri = res.redirection 161 | is_ssl = false 162 | if redir_uri.scheme == "https" 163 | port = URI::HTTPS::DEFAULT_PORT 164 | is_ssl = true 165 | end 166 | url = get_url(redir_uri.path, is_ssl) 167 | print_status("Redirection to: #{url} : ERR: #{res.code}") if datastore['VERBOSE'] 168 | path_search(path, url, port, redir_depth + 1) 169 | end 170 | end 171 | 172 | print_error("#{url} : #{res.code}") if datastore['DISPLAY_ERR'] 173 | return false 174 | end 175 | 176 | def skip_type(path) 177 | if path =~ datastore['EXCLUDE_NAMES'] 178 | return true 179 | end 180 | return false 181 | end 182 | 183 | def search_paths(paths) 184 | return nil if not paths 185 | 186 | found = 0 187 | for chunk in paths 188 | if skip_type(chunk) 189 | next 190 | end 191 | url = get_url(chunk, datastore['SSL']) 192 | pass = path_search(chunk, url, datastore['RPORT']) 193 | if pass 194 | found += 1 195 | end 196 | end 197 | print_status("Found paths: #{found} out of #{paths.length}") 198 | end 199 | 200 | def autoenable_ssl(port) 201 | old_ssl = datastore['SSL'] 202 | if (port == URI::HTTP::DEFAULT_PORT) 203 | datastore['SSL'] = false 204 | elsif (port == URI::HTTPS::DEFAULT_PORT) 205 | datastore['SSL'] = true 206 | end 207 | return old_ssl 208 | end 209 | 210 | # MSF API: 211 | 212 | def run 213 | paths = nil 214 | verbose = datastore['VERBOSE'] 215 | print_status("Verbose mode: #{verbose}") 216 | 217 | if datastore['PATH'] and datastore['PATH'].length > 0 218 | fname = datastore['PATH'] 219 | print_status("Searching by list, PATH = #{fname}") 220 | paths = read_file(fname) 221 | print_status("Attempting to search paths...") 222 | search_paths(paths) 223 | else 224 | print_error('PATH not set!') 225 | end 226 | 227 | if datastore['CMS_DIR'] and datastore['CMS_DIR'].length > 0 228 | cms = datastore['CMS_DIR'] 229 | print_status("Searching by CMS_DIR = #{cms}") 230 | paths = read_dir(cms) 231 | search_paths(paths) 232 | else 233 | print_error('CMS_DIR not set!') 234 | end 235 | 236 | end 237 | 238 | end 239 | 240 | --------------------------------------------------------------------------------