├── README ├── nessus_scripts ├── README └── nessus_http_api.rb └── nmap_scripts ├── README ├── livehosts.rb ├── nseoutput.rb ├── parsenmap.rb └── pingsweep.rb /README: -------------------------------------------------------------------------------- 1 | A collection of useful scripts for penetration testers 2 | -------------------------------------------------------------------------------- /nessus_scripts/README: -------------------------------------------------------------------------------- 1 | This repository is a collection of useful nessus scripts that will help assist on penetration tests 2 | 3 | This script accepts a file of IP addresses to be used for external testing. 4 | The file is parsed and nmap run against the targets on off hours creating 5 | output files which are uploaded to nessus. Nessus scans are then kicked 6 | off and when done results are emailed back to the user. 7 | 8 | Usage: ruby nessus_http_api.rb [options] 9 | -v, --verbose Output more information 10 | -n, --server IP The IP address of the Nessus server 11 | --port PORT The port of the Nessus server 12 | -u, --username USER Username of Nessus account 13 | -p, --password PASS Password to Nessus account 14 | -c, --client CLIENTNAME The name of the client 15 | -e, --email EMAIL The email to return results to 16 | -s, --sleep Sleep tille 6pm 17 | -a, --attach Attaches the Nessus file to email 18 | -h, --help Display this screen 19 | 20 | -------------------------------------------------------------------------------- /nessus_scripts/nessus_http_api.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby -w 2 | ############################################################################## 3 | # Usage: ruby nessus_http_api.rb 4 | # 5 | # This script accepts a file of IP addresses to be used for external testing. 6 | # The file is parsed and nmap run against the targets on off hours creating 7 | # output files which are uploaded to nessus. Nessus scans are then kicked 8 | # off and when done results are emailed back to the user. 9 | # 10 | # Written by smilingraccoon@gmail.com 11 | ############################################################################## 12 | require 'uri' 13 | require 'net/https' 14 | require 'net/smtp' 15 | require 'openssl' 16 | require 'optparse' 17 | 18 | ## Constant variables, change as you see fit 19 | MAIL_SERVER = 'smtp.comcast.net' 20 | FROMEMAIL = 'scans@domain.com' 21 | BOUNDARY = "---------------------------BOUNDARY--1234567890" 22 | 23 | # require program to run as root 24 | if ENV["USER"] != "root" 25 | puts "\n[-] Must run as root\n\n" 26 | exit 27 | end 28 | 29 | options = {} 30 | optparse = OptionParser.new do |opts| 31 | opts.banner = "\r\nhttp://www.pentestgeek.com/ - Written by smilingraccoon and Zeknox\r\n\r\n" 32 | opts.banner += "Usage: ruby nessus_http_api.rb [options] \r\n" 33 | opts.banner += "Example: nessus_http_api.rb -u root -p toor -c pentestgeek.com -e smilingraccoon@gmail.com targets.txt\r\n\r\n" 34 | options[:server] = "https://127.0.0.1:8834/" 35 | options[:port] = "8834" 36 | options[:username] = "root" 37 | options[:password] = nil 38 | options[:client] = "Clientname" 39 | # Removes sleep till off hours and displays HTTP reponse codes 40 | options[:verbose] = false 41 | options[:sleep] = false 42 | options[:email] = nil 43 | options[:policy] = "Internal Network Scan" 44 | options[:attachment] 45 | 46 | opts.on( '-v', '--verbose', 'Output more information' ) do 47 | options[:verbose] = true 48 | end 49 | 50 | opts.on( '-n', '--server IP', 'The IP address of the Nessus server' ) do |url| 51 | options[:server] = "https://" + url + ":8834/" 52 | end 53 | 54 | opts.on( '--port PORT', 'The port of the Nessus server' ) do |url| 55 | options[:port] = "https://" + url + ":8834/" 56 | end 57 | 58 | opts.on( '-u', '--username USER', 'Username of Nessus account' ) do |user| 59 | options[:username] = user 60 | end 61 | 62 | opts.on( '-p', '--password PASS', 'Password to Nessus account' ) do |pass| 63 | options[:password] = pass 64 | end 65 | 66 | opts.on( '-c', '--client CLIENTNAME', 'The name of the client' ) do |client| 67 | options[:client] = client 68 | end 69 | 70 | opts.on( '-e', '--email EMAIL', 'The email to return results to' ) do |email| 71 | options[:email] = email 72 | end 73 | 74 | opts.on( '-s', '--sleep', 'Sleep tille 6pm' ) do 75 | options[:sleep] = true 76 | end 77 | 78 | opts.on( '-a', '--attach', 'Attaches the Nessus file to email' ) do 79 | options[:attachment] = true 80 | end 81 | 82 | opts.on_tail( '-h', '--help', 'Display this screen' ) do 83 | puts opts 84 | exit 85 | end 86 | 87 | opts.parse! 88 | end 89 | 90 | # make sure argument was passed to script 91 | unless ARGV.length > 0 92 | puts "Usage: ruby nessus_http_api.rb " 93 | exit! 94 | else 95 | log = '' 96 | targetFile = File.new(ARGV[0], "r") 97 | targetList = targetFile.read 98 | log << "Target list is: \n#{targetList}" 99 | end 100 | 101 | def make_connection (req, options) 102 | attempts = 0 103 | begin 104 | response = $https.request(req) 105 | rescue Exception 106 | attempts = attempts + 1 107 | puts "\n[-] Connect could not be established...retrying" 108 | retry unless attempts > 2 109 | puts "\n[-] Cannot connect, check #{options[:server]}" unless attempts < 2 110 | exit 111 | end 112 | print response if options[:verbose] 113 | return response 114 | end 115 | 116 | # Grab directory name to be used in naming files 117 | #customerName = /201[2-9]\/(.*?)\/.*/.match(File.absolute_path(targetFile))[1] 118 | # Sleep till off hours 119 | def delay_start() 120 | time = Time.new 121 | if time.hour < 17 122 | puts "Sleeping until off hours...#{17 - time.hour}:#{sprintf("%02d",60 - time.min)} to go" 123 | sleep 3600*(17 - time.hour) - 60*(time.min) 124 | end 125 | end 126 | 127 | def time() 128 | Time.new 129 | end 130 | 131 | # Kick off nmap scans 132 | def execute_nmap(targetFile, client) 133 | begin 134 | time = time() 135 | nmapStart = time.inspect 136 | dir = ""+File.absolute_path(targetFile).chomp(File.basename(targetFile)) 137 | targetFile.close 138 | system("if [ ! -d \"#{dir}nmap\" ]; then mkdir #{dir}nmap; fi") 139 | system("nmap --open -sT -n -r -vvv -P0 -oA #{dir}nmap/#{client+".log"} -iL #{File.absolute_path(targetFile)}") 140 | nmapScan = "#{dir}nmap/#{client}.log.xml" 141 | time = time() 142 | nmapDone = time.inspect 143 | return nmapStart, nmapDone, nmapScan, dir 144 | rescue 145 | puts "[-] We had issues running nmap" 146 | puts "[-] Exiting program" 147 | exit! 148 | end 149 | end 150 | 151 | # method to login to nessus and obtain a token 152 | def nessus_login(log, options) 153 | begin 154 | # Parses URL 155 | url = URI.parse(options[:server]) 156 | # Make HTTP post request to login page. 157 | req = Net::HTTP::Post.new(url.path + "login") 158 | # Set post data to login information. 159 | req.set_form_data({ "login" => options[:username], "password" => options[:password] }) 160 | # Creates HTTPS for communication 161 | $https = Net::HTTP.new( url.host, url.port ) 162 | $https.use_ssl = true 163 | $https.verify_mode = OpenSSL::SSL::VERIFY_NONE 164 | # Makes the connection to nessus and logs in. Return data from server is parse to acquire token. 165 | response = make_connection(req, options) 166 | token = /(.*)<\/token>/.match(response.body)[1] 167 | log << "Token is: #{token}\n" 168 | # Makes new req to get policy list 169 | req = Net::HTTP::Post.new(url.path + "policy/list") 170 | req.set_form_data({ "token" => token }) 171 | return req, url, token 172 | rescue Exception => e 173 | puts e.message 174 | puts "[-] We had issues logging into nessus" 175 | puts "[-] Exiting program" 176 | exit! 177 | end 178 | end 179 | 180 | # Sleep till off hours 181 | delay_start if options[:sleep] 182 | 183 | # run nmap against targetFile 184 | puts "[+] Executing nmap against hosts" 185 | nmapStart, nmapDone, nmapScan, dir = execute_nmap(targetFile, options[:client]) 186 | puts "\n[+] Completed executing nmap against hosts" 187 | 188 | # login to nesus with credentials 189 | puts "[+] Logging into Nessus with crednetials" 190 | req, url, token = nessus_login(log, options) 191 | 192 | policyID = '' 193 | # Grabs the policy, iterate the lines and looks for the policy name. 194 | # When found sets previous policyID to var to be used when policy id is required by nessus. 195 | make_connection(req, options).body.each_line do |line| 196 | next unless line =~ /policyID|policyName/ 197 | if line =~ /policyID/ 198 | policyID = /(.*)<\/policyID>/.match(line)[1] 199 | else 200 | policyName = /(.*)<\/policyName>/.match(line)[1] 201 | if policyName == options[:policy] 202 | break 203 | end 204 | end 205 | end 206 | 207 | # Makes new req to upload the XML file from nmap 208 | req = Net::HTTP::Post.new(url.path + "file/upload") 209 | 210 | # Creates the body of the HTTPS request and sets proper HTTP content parameters for file 211 | 212 | post_body = [] 213 | post_body << "--#{BOUNDARY}\r\n" 214 | post_body << "Content-Disposition: form-data; name=\"Filedata\"; filename=\"#{File.basename(nmapScan)}\"\r\n" 215 | post_body << "Content-Type: text/xml\r\n" 216 | post_body << "\r\n" 217 | post_body << File.read(nmapScan) 218 | post_body << "\r\n--#{BOUNDARY}--\r\n" 219 | 220 | # Adding parameters to the HTTP header 221 | req["Content-Type"] = "multipart/form-data; boundary=#{BOUNDARY}" 222 | req["Cookie"] = "token=#{token}" 223 | req.body = post_body.join 224 | 225 | #req.each_header {|key,value| puts "#{key} = #{value}"} 226 | make_connection(req, options) 227 | 228 | # Download a copy of the report 229 | req = Net::HTTP::Post.new(url.path + "policy/download") 230 | req.set_form_data({ "token" => token, "policy_id" => policyID }) 231 | 232 | # Set up parameters to pull correct policy 233 | policySettings = {} 234 | policySettings["token"] = token 235 | policySettings["policy_id"] = policyID 236 | policySettings["policy_name"] = options[:policy] 237 | 238 | # Booleans to look for tags within the nessus configuration xml, first section is server preferences 239 | serverPreferences = true 240 | pluginPreferences = false 241 | familySelection = false 242 | individualPlugin = false 243 | tempKey = '' 244 | make_connection(req, options).body.each_line do |line| 245 | if line =~ /^<\/ServerPreferences>$/ 246 | serverPreferences = false 247 | elsif line =~ /^$/ 248 | pluginPreferences = true 249 | elsif line =~ // 250 | familySelection = true 251 | pluginPreferences = false 252 | elsif line =~ // 253 | individualPlugin = true 254 | familySelection = false 255 | end 256 | # If still in server preferences section, look for name and value and add to hash 257 | if(serverPreferences) 258 | if line =~ /.*<\/name>/ 259 | tempKey = /(.*)<\/name>/.match(line)[1] 260 | elsif line =~ /.*<\/value>/ 261 | policySettings[tempKey] = /(.*)<\/value>/.match(line)[1] 262 | end 263 | # If in plugins preferences section, look for fullName and preferenceValue and add to hash 264 | elsif(pluginPreferences) 265 | if line =~ /.*<\/fullName>/ 266 | tempKey = /(.*)<\/fullName>/.match(line)[1] 267 | elsif line =~ /.*<\/preferenceValue[s]{0,1}>/ 268 | policySettings[tempKey] = /(.*)<\/preferenceValue[s]{0,1}>/.match(line)[1] 269 | end 270 | # If in family selection section, look for FamilyName and Status and add to hash 271 | elsif(familySelection) 272 | if line =~ /.*<\/FamilyName>/ 273 | tempKey = "plugin_selection.family." + /(.*)<\/FamilyName>/.match(line)[1] 274 | elsif line =~ /.*<\/Status>/ 275 | policySettings[tempKey] = /(.*)<\/Status>/.match(line)[1] 276 | end 277 | elsif(individualPlugin) 278 | if line =~ /.*<\/PluginId>/ 279 | tempKey = "plugin_selection.individual_plugin." + /(.*)<\/PluginId>/.match(line)[1] 280 | elsif line =~ /.*<\/Status>/ 281 | policySettings[tempKey] = /(.*)<\/Status>/.match(line)[1] 282 | end 283 | end 284 | end 285 | 286 | # Makes new req to edit policy 287 | req = Net::HTTP::Post.new(url.path + "policy/edit") 288 | 289 | # Make the change to the Nmap nasl with new file. 290 | policySettings["Nmap (XML file importer)[file]:File containing XML results :"] = File.basename(nmapScan) 291 | req.set_form_data(policySettings) 292 | 293 | # Log settings used and actual URL body for edit policy 294 | nessus_log = '' 295 | policySettings.each { |key, value| nessus_log << "#{key} is #{value}\n"} 296 | log << "HTTP Request for edit is: \n#{req.body}" 297 | 298 | make_connection(req, options) 299 | 300 | targetList.tr_s('\n',',') 301 | targetList.gsub(/\s/, ",") 302 | 303 | 304 | # Setting up scan based on previously acquired policy ID and passes it nmap file name to use for report name. 305 | req = Net::HTTP::Post.new(url.path + "scan/new") 306 | req.set_form_data({ "token" => token, "policy_id" => policyID, \ 307 | "scan_name" => File.basename(nmapScan).chomp(".log.xml"), \ 308 | "target" => targetList }) 309 | 310 | time = Time.new 311 | scanStart = time.inspect 312 | uuid = '' 313 | # Starts the scan, interates response and grabs the UUID of the scan for later use. 314 | make_connection(req, options).body.each_line do |line| 315 | next unless line =~ /uuid/ 316 | uuid = /(.*)<\/uuid>/.match(line)[1] 317 | end 318 | 319 | req = Net::HTTP::Post.new(url.path + "scan/list") 320 | req.set_form_data({ "token" => token }) 321 | 322 | print "\nStarting nessus scan" 323 | # While the uuid of the scan is showing up in the scan lists sleep until it is no longer there (scan done) 324 | while (make_connection(req, options).body =~ /#{uuid}<\/uuid>/) 325 | 3.times do 326 | sleep 10 327 | print "." 328 | end 329 | end 330 | # Timestamp for when the report finished 331 | time = Time.new 332 | scanDone = time.inspect 333 | puts "\nScan done at #{scanDone}" 334 | 335 | # Download the report 336 | req = Net::HTTP::Post.new(url.path + "file/report/download") 337 | req.set_form_data({ "token" => token , "report" => uuid}) 338 | report = make_connection(req, options).body 339 | 340 | # Setting up var scope 341 | reportMsg = '' 342 | currentHost = '' 343 | hostArray = Array.new 344 | overallRating = {"critical" => 0,"high" => 0,"medium" => 0,"low" => 0,"info" => 0} 345 | rating = {"critical" => 0,"high" => 0,"medium" => 0,"low" => 0,"info" => 0} 346 | hostCount = 0 347 | portCount = 0 348 | # For the downloaded report, parse it by host and pull statistics out 349 | report.each_line do |line| 350 | # Find host IP, increase count by one, and reset the rating stats 351 | if line =~ // 352 | currentHost = //.match(line)[1] 353 | rating = {"critical" => 0,"high" => 0,"medium" => 0,"low" => 0,"info" => 0} 354 | hostCount += 1 355 | # Find end of host, write rating stats to var and re initial array of ports 356 | elsif line =~ /<\/ReportHost>/ 357 | reportMsg << "IP: #{sprintf("%15s", currentHost)}\tC: #{sprintf("%2d", rating["critical"])}\tH: #{sprintf("%2d", rating["high"])}\tM: #{sprintf("%2d", rating["medium"])}\tL: #{sprintf("%2d", rating["low"])}\tI: #{sprintf("%2d", rating["info"])}\tPorts: #{hostArray.sort.join(", ")}\n" 358 | portCount += hostArray.length 359 | hostArray = Array.new 360 | # Find line with port and finding, add port to array and rating to hashes of device and overall ratings 361 | elsif line =~ / 393 | To: Tom <#{options[:email]}> 394 | Subject: External report done for #{options[:client]} 395 | MIME-Version: 1.0 396 | Content-Type: multipart/mixed; boundary=#{BOUNDARY} 397 | 398 | --#{BOUNDARY} 399 | Content-Type: text/plain 400 | Content-Transfer-Encoding:8bit 401 | 402 | Nmap started:\t#{nmapStart} 403 | Nmap ended:\t#{nmapDone} 404 | 405 | Scan started:\t#{scanStart} 406 | Scan ended:\t#{scanDone} 407 | 408 | Hosts scanned: \t#{hostCount}\t\tPorts scanned:#{sprintf("%2d", portCount)} 409 | Total findings:\t\tC: #{sprintf("%2d", overallRating["critical"])}\tH: #{sprintf("%2d", overallRating["high"])}\tM: #{sprintf("%2d", overallRating["medium"])}\tL: #{sprintf("%2d", overallRating["low"])}\tI: #{sprintf("%2d", overallRating["info"])} 410 | 411 | #{reportMsg} 412 | EOF 413 | 414 | # Encode the report in base64 415 | encodedReport = [report].pack("m") if options[:attachment] 416 | 417 | # Build attachment section of email and attach fil e 418 | attachment =< e 438 | print "Exception occured: #{e}" 439 | end 440 | 441 | logFile = File.new("#{dir}script.log",'w') 442 | logFile.write(log) 443 | logFile.close 444 | 445 | logFile = File.new("#{dir}nessus.log",'w') 446 | logFile.write(nessus_log) 447 | logFile.close -------------------------------------------------------------------------------- /nmap_scripts/README: -------------------------------------------------------------------------------- 1 | This repository is a collection of nmap scripts that are useful for penetration 2 | testing engagements. Some scripts are use to automate large tasks while others 3 | are used to parse nmap output. 4 | 5 | Some of the scripts may require that specific ruby gems are installed for proper 6 | functionality. 7 | 8 | If you have any questions please contact us. 9 | -------------------------------------------------------------------------------- /nmap_scripts/livehosts.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | ################## 3 | #File = livehosts.rb 4 | #Author = Brandon McCann 5 | #Last edited 8/7/2012 6 | 7 | require 'rubygems' 8 | require 'nmap/parser' 9 | 10 | # make sure argument was passed to script 11 | unless ARGV.length > 0 12 | puts "\nlivehosts.rb will take an nmap xml file and list live hosts to stdout \n" 13 | puts "\nUSAGE:\t./livehosts.rb \r\n\n" 14 | exit! 15 | end 16 | 17 | # parse the xml file for only hosts alive 18 | parser = Nmap::Parser.parsefile("#{ARGV[0]}") 19 | parser.hosts("up") do |host| 20 | puts "#{host.addr}" 21 | end 22 | -------------------------------------------------------------------------------- /nmap_scripts/nseoutput.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | ################## 3 | #File = livehosts.rb 4 | #Author = Brandon McCann 5 | #Last edited 10/2/2012 6 | 7 | require 'rubygems' 8 | require 'nmap/parser' 9 | 10 | # make sure argument was passed to script 11 | unless ARGV.length > 0 12 | puts "\nthis will list output of NSE scripts\n" 13 | puts "\nUSAGE:\t./nseoutput.rb \r\n\n" 14 | exit! 15 | end 16 | 17 | # parse the xml file for all scripts run 18 | parser = Nmap::Parser.parsefile("#{ARGV[0]}") 19 | parser.hosts do |host| 20 | host.scripts do | script| 21 | puts "[+] " + host.addr + " " + script.id + " " + script.output 22 | end 23 | end -------------------------------------------------------------------------------- /nmap_scripts/parsenmap.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | ################## 3 | #File = parsenmap 4 | #Author = Royce Davis 5 | #Last edited February 2, 2011 6 | require 'rubygems' 7 | require 'nmap/parser' 8 | 9 | unless ARGV.length > 0 10 | puts "USAGE:\t./parsenmap.rb \r\n\n" 11 | exit! 12 | end 13 | 14 | parser = Nmap::Parser.parsefile("#{ARGV[0]}") 15 | parser.hosts("up") do |host| 16 | [:tcp, :udp].each do |type| 17 | host.getports(type, "open") do |port| 18 | string = port.state.to_s 19 | unless string.include? "filtered" 20 | srv = port.service 21 | puts "#{host.addr}\t#{port.num}\t#{srv.name}\t#{srv.product} #{srv.version}" 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /nmap_scripts/pingsweep.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | # 3 | # Author: zeknox - www.pentestgeek.com 4 | # 5 | # Description: This script will pingsweep all networks defined 6 | # in and output live systems to hosts 7 | # folder and raw nmap output is placed in pingsweep 8 | # folder that script was run from 9 | # 10 | ####################################################################### 11 | require 'netaddr' 12 | 13 | # require program to run as root 14 | if ENV["USER"] != "root" 15 | puts "\n[-] must run as root\n\n" 16 | exit 17 | end 18 | 19 | # check to make sure an argument was passed 20 | if ARGV.size != 1 then 21 | puts "\n[-] Usage: ./start.rb \n\n" 22 | exit 23 | end 24 | 25 | # import subnets from file into array 26 | subnets = [] 27 | File.open(ARGV[0] , "r") do |file| 28 | while (subnet = file.gets) 29 | # validate all networks are cidr format 30 | if not subnet =~ /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}\b/ 31 | puts "[-] #{subnet.chomp} is not a valid subnet" 32 | exit 33 | end 34 | subnets << subnet 35 | end 36 | end 37 | 38 | # check if folder structure is created, if not create it 39 | def check_exists(folder) 40 | if File.exists?(folder) 41 | return true 42 | else 43 | return false 44 | end 45 | end 46 | 47 | def create_folder(folder) 48 | system("mkdir #{folder}") 49 | end 50 | 51 | ["pingsweep","hosts"].each do |folder| 52 | if !check_exists(folder) 53 | create_folder(folder) 54 | end 55 | end 56 | 57 | # method to convert cidr to network 58 | def cidr_convert(net) 59 | net = NetAddr::CIDR.create("#{net.chomp}") 60 | net.network 61 | end 62 | 63 | # method to pingsweep network using nmap 64 | def pingsweep(subnet) 65 | net = cidr_convert("#{subnet}") 66 | puts "[+] Running pingsweep scan against #{subnet.chomp}" 67 | system("nmap -sP #{subnet.chomp} -oA pingsweep/#{net}_sweep > /dev/null 2>&1") 68 | end 69 | 70 | # method to find live hosts 71 | def livehosts(subnet) 72 | hosts = [] 73 | net = cidr_convert("#{subnet}") 74 | File.open("pingsweep/#{net}_sweep.gnmap" , "r") do |file| 75 | # place only live ips in host array 76 | while (line = file.gets) 77 | next unless line.match(/Up/) 78 | hosts << line.split(' ').to_s.match(/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/) 79 | end 80 | 81 | # write live hosts to a file hosts.txt 82 | if hosts.empty? 83 | puts "[-] No live hosts in network: #{subnet.chomp}" 84 | else 85 | puts "[+] #{hosts.length} live hosts in #{subnet.chomp}" 86 | puts "[+] Writing live hosts to file: #{Dir.pwd}/hosts/#{net}_hosts.txt\n" 87 | 88 | hosts.each do |host| 89 | File.open("hosts/#{net}_hosts.txt" , 'a+') {|f| f.write("#{host}\n") } 90 | end 91 | end 92 | end 93 | end 94 | 95 | subnets.each do |subnet| 96 | # pingsweep each subnet 97 | pingsweep(subnet) 98 | 99 | # write livehosts to file 100 | livehosts(subnet) 101 | end --------------------------------------------------------------------------------