├── README.beef ├── lib └── beef │ ├── remote.rb │ └── remote │ ├── base.rb │ ├── command.rb │ ├── session.rb │ └── zombie-poll.rb └── plugins └── beef.rb /README.beef: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2011 Wade Alcorn wade@bindshell.net 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | ================================================ 17 | 18 | Remote BeEF Metasploit Plugin 19 | 20 | Version 0.1 - 31st July 2011 21 | 22 | 0. Dependencies 23 | a) Apart from "metasploit", you'll also need to install the following gems. 24 | b) # gem install hpricot 25 | c) # gem install json 26 | 27 | 1. Installation & Usage 28 | a) Copy these files into your metasploit folder (this should copy beef.rb into your msf/plugins/ folder, and the beef/ folder into your msf/lib/ folder) 29 | b) Start metasploit 30 | c) msf > load beef 31 | [*] BeEF Bridge for Metasploit 0.1 32 | [+] Type beef_help for a command listing 33 | [*] Successfully loaded plugin: beef 34 | d) msf > beef_help 35 | 36 | Command Help text 37 | ------- --------- 38 | Generic Commands 39 | ----------------- ----------------- 40 | beef_connect Connect to a remote BeEF server. 41 | beef_disconnect Disconnect from the remote BeEF server. 42 | beef_help Display this help listing. 43 | 44 | Hooked Browser Commands 45 | ----------------- ----------------- 46 | beef_online List available hooked browsers and their details. 47 | beef_offline List previously hooked browsers and their details. 48 | beef_import Import available hooked browsers into db_hosts. 49 | beef_review Review previously hooked browsers within BeEF. 50 | 51 | e) If you want to "beef_import", you'll need to db_connect first 52 | -------------------------------------------------------------------------------- /lib/beef/remote.rb: -------------------------------------------------------------------------------- 1 | require 'beef/remote/base' 2 | require 'beef/remote/session' 3 | require 'beef/remote/zombie-poll' 4 | require 'beef/remote/command' -------------------------------------------------------------------------------- /lib/beef/remote/base.rb: -------------------------------------------------------------------------------- 1 | module BeEF 2 | module Remote 3 | 4 | class Base 5 | attr_accessor :session 6 | attr_accessor :zombiepoll 7 | attr_accessor :command 8 | attr_accessor :jobs 9 | attr_reader :targetsession 10 | attr_reader :target 11 | attr_reader :targetip 12 | 13 | def initialize() 14 | self.session = BeEF::Remote::Session.new 15 | self.zombiepoll = BeEF::Remote::ZombiePoll.new(self.session) 16 | self.command = BeEF::Remote::Command.new(self.session) 17 | self.jobs = Rex::JobContainer.new 18 | end 19 | 20 | def settarget(id) 21 | self.targetsession = self.zombiepoll.getsession(id) 22 | self.targetip = self.zombiepoll.getip(id) 23 | self.target = id 24 | end 25 | 26 | def setofflinetarget(id) 27 | self.targetsession = self.zombiepoll.getofflinesession(id) 28 | self.targetip = "(OFFLINE) " + self.zombiepoll.getofflineip(id) 29 | self.target = id 30 | end 31 | 32 | def cleartarget 33 | self.targetsession = nil 34 | self.target = nil 35 | self.targetip = nil 36 | end 37 | 38 | protected 39 | 40 | attr_writer :target 41 | attr_writer :targetsession 42 | attr_writer :targetip 43 | attr_writer :command 44 | end 45 | 46 | end end -------------------------------------------------------------------------------- /lib/beef/remote/command.rb: -------------------------------------------------------------------------------- 1 | module BeEF 2 | module Remote 3 | 4 | class Command 5 | 6 | attr_reader :cmd 7 | 8 | def initialize(session) 9 | self.session = session 10 | self.cmd = {} 11 | end 12 | 13 | def getcommands(session) 14 | self.session.getjson("/ui/modules/select/commandmodules/tree.json",{"zombie_session"=>session.to_s}) 15 | end 16 | 17 | def setmodule(id) 18 | tmp = self.session.getjson("/ui/modules/select/commandmodule.json",{"command_module_id"=>id.to_s}) 19 | self.cmd['id'] = id.to_s 20 | self.cmd['Name'] = tmp['command_modules']['1']['Name'].to_s 21 | self.cmd['Description'] = tmp['command_modules']['1']['Description'].to_s 22 | self.cmd['Category'] = tmp['command_modules']['1']['Category'].to_s 23 | self.cmd['Data'] = tmp['command_modules']['1']['Data'] 24 | end 25 | 26 | def clearmodule 27 | self.cmd = {} 28 | end 29 | 30 | def setparam(param,value) 31 | self.cmd['Data'].each do |data| 32 | if data['name'] == param 33 | data['value'] = value 34 | return 35 | end 36 | end 37 | end 38 | 39 | def runmodule(session) 40 | options = {} 41 | options.store("zombie_session", session.to_s) 42 | options.store("command_module_id", self.cmd['id']) 43 | options.store("nonce",self.session.nonce.to_s) 44 | 45 | if not self.cmd['Data'].nil? 46 | self.cmd['Data'].each do |key| 47 | options.store("txt_"+key['name'].to_s,key['value']) 48 | end 49 | end 50 | 51 | resp = self.session.getraw("/ui/modules/commandmodule/new", options) 52 | if resp.body == "{success : true}" 53 | ret = "true" 54 | else 55 | ret = nil 56 | end 57 | ret 58 | end 59 | 60 | def getcmdexeccount(session,cmdid) 61 | json = self.session.getjson("/ui/modules/commandmodule/commands.json", {"zombie_session"=>session.to_s,"command_module_id"=>cmdid,"nonce"=>self.session.nonce.to_s}) 62 | json['commands'].length 63 | end 64 | 65 | def getcmdresponses(session) 66 | self.session.getjson("/ui/modules/commandmodule/commands.json",{"zombie_session"=>session.to_s,"command_module_id"=>self.cmd['id'],"nonce"=>self.session.nonce.to_s}) 67 | end 68 | 69 | def getindividualresponse(cmdid) 70 | begin 71 | return self.session.getjson("/ui/modules/select/command_results.json",{"command_id"=>cmdid}) 72 | rescue 73 | return nil 74 | end 75 | end 76 | 77 | protected 78 | attr_writer :cmd 79 | attr_accessor :session 80 | end 81 | 82 | end end -------------------------------------------------------------------------------- /lib/beef/remote/session.rb: -------------------------------------------------------------------------------- 1 | module BeEF 2 | module Remote 3 | 4 | class Session 5 | 6 | require 'net/http' 7 | require 'rubygems' 8 | require 'json' 9 | require 'hpricot' 10 | require 'open-uri' 11 | 12 | attr_reader :cookie 13 | attr_reader :baseuri 14 | attr_reader :connected 15 | attr_reader :nonce 16 | 17 | def authenticate(baseuri,username,password) 18 | self.baseuri = baseuri 19 | url = self.baseuri+"/ui/authentication/login" 20 | resp = Net::HTTP.post_form(URI.parse(url),{'username-cfrm'=>username,'password-cfrm'=>password}) 21 | 22 | if resp.body == "{ success : true }" 23 | self.cookie = resp.response['set-cookie'] 24 | 25 | #Get the nonce 26 | doc = Hpricot(self.getraw("/ui/panel").body) 27 | self.nonce = doc.at("input#nonce")[:value] 28 | self.connected = true 29 | else 30 | return nil 31 | end 32 | end 33 | 34 | def getraw(req,opts = nil) 35 | url = self.baseuri + req 36 | uri = URI.parse(url) 37 | 38 | http = Net::HTTP.new(uri.host,uri.port) 39 | if not opts.nil? 40 | request = Net::HTTP::Post.new(uri.request_uri) 41 | request.set_form_data(opts) 42 | else 43 | request = Net::HTTP::Get.new(uri.request_uri) 44 | end 45 | request.initialize_http_header({"Cookie" => self.cookie}) 46 | response = http.request(request) 47 | end 48 | 49 | def getjson(req,opts = nil) 50 | JSON.parse(self.getraw(req,opts).body) 51 | end 52 | 53 | def initialize 54 | self.cookie = nil 55 | self.baseuri = nil 56 | self.connected = nil 57 | end 58 | 59 | def disconnect 60 | self.cookie = nil 61 | self.connected = nil 62 | self.baseuri = nil 63 | end 64 | 65 | private 66 | 67 | attr_writer :cookie 68 | attr_writer :baseuri 69 | attr_writer :connected 70 | attr_writer :nonce 71 | 72 | end 73 | 74 | end end -------------------------------------------------------------------------------- /lib/beef/remote/zombie-poll.rb: -------------------------------------------------------------------------------- 1 | module BeEF 2 | module Remote 3 | 4 | class ZombiePoll 5 | 6 | #include Rex::Ui::Subscriber 7 | #the above should allow the standard print_status etc messages, but this isn't working pretty yet 8 | #Actually, the MSF framework uses a clever subscriber system, I'm just hacking this by setting a local instance of the 9 | # Rex output :P 10 | 11 | #attr_accessor :session 12 | attr_reader :hb 13 | 14 | def initialize(session) 15 | self.session = session 16 | end 17 | 18 | def hooked 19 | self.hb = self.session.getjson("/ui/panel/hooked-browser-tree-update.json") 20 | end 21 | 22 | def hookedpoll(ctx) 23 | ctx.print_line 24 | ctx.print_status("Starting the online zombie poll-er") 25 | 26 | hooked 27 | ctx.print_status("Initial zombies:") 28 | 29 | self.hb['hooked-browsers']['online'].each{|x| 30 | ctx.print_line(x[0]+": "+x[1]['browser_icon']+" on "+x[1]['os_icon']+" in the domain: "+x[1]['domain']+" with the ip: "+x[1]['ip']) 31 | } 32 | 33 | while(true) 34 | oldhb = self.hb 35 | 36 | hooked 37 | if oldhb == self.hb 38 | #ctx.print_status("the same") #Perhaps print something if there's a verbose option set? 39 | else 40 | ctx.print_status("different!") 41 | if oldhb['hooked-browsers']['online'].length < self.hb['hooked-browsers']['online'].length #we have a NEW HB 42 | ctx.print_status("We got a NEW HB") 43 | tmp = self.hb['hooked-browsers']['online'].select{|x,y| !oldhb['hooked-browsers']['online'].include?(x)} 44 | tmp.each{ |x| 45 | ctx.print_line(x[1]['browser_icon']+" on "+x[1]['os_icon']+" in the domain: "+x[1]['domain']+" with the ip: "+x[1]['ip']) 46 | } 47 | elsif oldhb['hooked-browsers']['online'].length > self.hb['hooked-browsers']['online'].length #we have lost a HB 48 | ctx.print_status("We lost a HB") 49 | tmp = oldhb['hooked-browsers']['online'].select{|x,y| !self.hb['hooked-browsers']['online'].include?(x)} 50 | tmp.each{ |x| 51 | ctx.print_line(x[1]['browser_icon']+" on "+x[1]['os_icon']+" in the domain: "+x[1]['domain']+" with the ip: "+x[1]['ip']) 52 | } 53 | else #we lost and got a new one 54 | ctx.print_status("The hell? I think we lost and gained a new HB in a single poll - TODO") 55 | end 56 | end 57 | select(nil,nil,nil,5) 58 | end 59 | end 60 | 61 | def getsession(id) 62 | if self.hb.nil? 63 | self.hooked 64 | end 65 | self.hb['hooked-browsers']['online'][id.to_s]['session'] 66 | end 67 | 68 | def getofflinesession(id) 69 | if self.hb.nil? 70 | self.hooked 71 | end 72 | self.hb['hooked-browsers']['offline'][id.to_s]['session'] 73 | end 74 | 75 | def getip(id) 76 | if self.hb.nil? 77 | self.hooked 78 | end 79 | self.hb['hooked-browsers']['online'][id.to_s]['ip'] 80 | end 81 | 82 | def getofflineip(id) 83 | if self.hb.nil? 84 | self.hooked 85 | end 86 | self.hb['hooked-browsers']['offline'][id.to_s]['ip'] 87 | end 88 | 89 | def getinfo(session) 90 | self.session.getjson("/ui/modules/select/zombie_summary.json",{"zombie_session"=>session}) 91 | end 92 | 93 | protected 94 | 95 | attr_writer :hb 96 | attr_accessor :session 97 | 98 | end 99 | 100 | end end -------------------------------------------------------------------------------- /plugins/beef.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'beef/remote' 4 | 5 | module Msf 6 | 7 | #Constants 8 | BeefVer = "0.1" 9 | 10 | class Plugin::Beef < Msf::Plugin 11 | 12 | class ConsoleCommandDispatcher 13 | include Msf::Ui::Console::CommandDispatcher 14 | 15 | def name 16 | "Beef" 17 | end 18 | 19 | def commands 20 | { 21 | "beef_connect" => "Connect to a remote BeEF server: beef_connect ", 22 | "beef_disconnect" => "Disconnect from the remote BeEF server", 23 | "beef_help" => "Get help on all commands", 24 | "beef_import" => "Import available hooked browsers into metasploit", 25 | "beef_online" => "List available hooked browsers", 26 | "beef_offline" => "List previously hooked browsers", 27 | "beef_review" => "Review a previously hooked browser", 28 | "beef_target" => "Target a currently hooked browser", 29 | } 30 | end 31 | 32 | @@beef_review_opts = Rex::Parser::Arguments.new( 33 | "-h" => [ false, "Help."], 34 | "-i" => [ true, "Display info about the offline hooked browser. \"beef_review -i \""], 35 | "-r" => [ true, "Review the response from a previously executed command module. \"beef_review -r ()\""]) 36 | 37 | @@beef_target_opts = Rex::Parser::Arguments.new( 38 | "-h" => [ false, "Help."], 39 | "-i" => [ true, "Display info about the online hooked browser (target). \"beef_target -i \""], 40 | "-r" => [ true, "Review the response from a previously executed command module. \"beef_target -r ()\""], 41 | "-c" => [ true, "List available commands for a particular target. \"beef_target -c ()\""], 42 | "-e" => [ true, "Execute a module against a target. \"beef_target -e \""]) 43 | 44 | #Thank you Nessus plugin for this! 45 | def beef_verify_db 46 | if ! (framework.db and framework.db.active) 47 | print_error("No database has been configured, please use db_create/db_connect first") 48 | return false 49 | end 50 | true 51 | end 52 | 53 | def beef_logo_to_os(logo) 54 | case logo 55 | when "mac.png" 56 | hbos = "Mac OS X" 57 | when "linux.png" 58 | hbos = "Linux" 59 | when "win.png" 60 | hbos = "Microsoft Windows" 61 | when "unknown.png" 62 | hbos = "Unknown" 63 | end 64 | end 65 | 66 | def cmd_beef_connect(*args) 67 | if (args[0] == nil or args[0] == "-h" or args[0] == "--help") 68 | cmd_beef_connect_help 69 | return 70 | end 71 | 72 | if args.length != 3 73 | cmd_beef_connect_help 74 | return 75 | end 76 | 77 | @remotebeef = BeEF::Remote::Base.new if not defined? @remotebeef 78 | 79 | if not @remotebeef.session.connected.nil? 80 | print_status("You are already connected") 81 | return 82 | end 83 | 84 | if (@remotebeef.session.authenticate(args[0], args[1],args[2]).nil?) 85 | #For some reason, the first attempt always fails, lets sleep for a couple of secs and try again 86 | select(nil,nil,nil,2) 87 | if (@remotebeef.session.authenticate(args[0], args[1], args[2]).nil?) 88 | print_status("Connection failed..") 89 | else 90 | print_status("Connected to "+args[0]) 91 | end 92 | else 93 | print_status("Connected to "+args[0]) 94 | end 95 | end 96 | 97 | def cmd_beef_connect_help 98 | print_status(" Usage: beef_connect ") 99 | print_status("Examples:") 100 | print_status(" beef_connect http://127.0.0.1:3000 beef beef") 101 | end 102 | 103 | def cmd_beef_disconnect(*args) 104 | if args[0] == "-h" 105 | cmd_beef_disconnect_help 106 | return 107 | end 108 | 109 | begin 110 | if @remotebeef.session.connected.nil? 111 | print_status("You aren't connected") 112 | else 113 | @remotebeef.session.disconnect 114 | print_status("You are now disconnected") 115 | end 116 | rescue 117 | print_status("You aren't connected") 118 | end 119 | end 120 | 121 | def cmd_beef_disconnect_help 122 | print_status("Disconnect from the remote BeEF instance") 123 | end 124 | 125 | def cmd_beef_help(*args) 126 | tbl = Rex::Ui::Text::Table.new( 127 | 'Columns' => 128 | [ 129 | 'Command', 130 | 'Help text' 131 | ]) 132 | tbl << [ "Generic Commands",""] 133 | tbl << [ "-----------------", "-----------------"] 134 | tbl << [ "beef_connect", "Connect to a remote BeEF server."] 135 | tbl << [ "beef_disconnect", "Disconnect from the remote BeEF server."] 136 | tbl << [ "beef_help", "Display this help listing."] 137 | tbl << ["",""] 138 | tbl << [ "Hooked Browser Commands",""] 139 | tbl << [ "-----------------", "-----------------"] 140 | tbl << [ "beef_online", "List available hooked browsers and their details."] 141 | tbl << [ "beef_offline","List previously hooked browsers and their details."] 142 | tbl << [ "beef_import", "Import available hooked browsers into db_hosts."] 143 | tbl << [ "beef_review", "Review previously hooked browsers within BeEF."] 144 | tbl << [ "beef_target", "Target a currently hooked browser within BeEF."] 145 | puts "\n" 146 | puts tbl.to_s + "\n" 147 | end 148 | 149 | def cmd_beef_import(*args) 150 | if ! beef_verify_db 151 | return 152 | end 153 | 154 | if args[0] == "-h" 155 | cmd_beef_import_help 156 | return 157 | end 158 | 159 | hb = nil 160 | begin 161 | if @remotebeef.session.connected.nil? 162 | print_status("You aren't connected") 163 | return 164 | else 165 | hb = @remotebeef.zombiepoll.hooked 166 | end 167 | rescue 168 | print_status("You don't appear to be connected") 169 | return 170 | end 171 | 172 | print_status("Importing hosts now...") 173 | 174 | hb['hooked-browsers']['online'].each{ |x| 175 | if x[1]['ip'].to_s == "127.0.0.1" 176 | print_status("Can't add self to db_hosts - skipping this entry") 177 | else 178 | framework.db.find_or_create_host({:host => x[1]['ip'], :os_name => beef_logo_to_os(x[1]['os_icon'].to_s) }) 179 | print_status("Added " + x[1]['ip']) 180 | end 181 | } 182 | 183 | print_status("Importation complete.") 184 | 185 | end 186 | 187 | def cmd_beef_import_help 188 | print_status("Import available hooked browsers into db_hosts.") 189 | end 190 | 191 | def cmd_beef_online(*args) 192 | hb = nil 193 | begin 194 | if @remotebeef.session.connected.nil? 195 | print_status("You aren't connected") 196 | return 197 | else 198 | hb = @remotebeef.zombiepoll.hooked 199 | end 200 | rescue 201 | print_status("You don't appear to be connected") 202 | return 203 | end 204 | 205 | tbl = Rex::Ui::Text::Table.new( 206 | 'Columns' => 207 | [ 208 | 'Id', 209 | 'IP', 210 | 'OS' 211 | ]) 212 | hb['hooked-browsers']['online'].each{ |x| 213 | tbl << [x[0].to_s , x[1]['ip'].to_s, beef_logo_to_os(x[1]['os_icon'].to_s)] 214 | } 215 | puts "\n" 216 | puts "Currently hooked browsers within BeEF" 217 | puts "\n" 218 | puts tbl.to_s + "\n" 219 | end 220 | 221 | def cmd_beef_offline(*args) 222 | hb = nil 223 | begin 224 | if @remotebeef.session.connected.nil? 225 | print_status("You aren't connected") 226 | return 227 | else 228 | hb = @remotebeef.zombiepoll.hooked 229 | end 230 | rescue 231 | print_status("You don't appear to be connected") 232 | return 233 | end 234 | 235 | tbl = Rex::Ui::Text::Table.new( 236 | 'Columns' => 237 | [ 238 | 'Id', 239 | 'IP', 240 | 'OS' 241 | ]) 242 | hb['hooked-browsers']['offline'].each{ |x| 243 | tbl << [x[0].to_s , x[1]['ip'].to_s, beef_logo_to_os(x[1]['os_icon'].to_s)] 244 | } 245 | puts "\n" 246 | puts "Previously hooked browsers within BeEF" 247 | puts "\n" 248 | puts tbl.to_s + "\n" 249 | end 250 | 251 | def cmd_beef_target(*args) 252 | if (args[0] == nil or args[0] == "-h") 253 | print_status("Listing online browsers...") 254 | cmd_beef_online 255 | cmd_beef_target_help 256 | return 257 | end 258 | 259 | if @remotebeef.session.connected.nil? 260 | print_status("You aren't connected") 261 | return 262 | end 263 | 264 | @@beef_target_opts.parse(args) {|opt, idx, val| 265 | case opt 266 | when "-i" 267 | if args.length < 2 268 | cmd_beef_target_help 269 | return 270 | end 271 | @remotebeef.settarget(val) 272 | info = @remotebeef.zombiepoll.getinfo(@remotebeef.targetsession) 273 | info['results'].each { |x| 274 | x['data'].each { |k,v| 275 | print_line(k+ " - "+v) 276 | } 277 | } 278 | return 279 | when "-r" 280 | if args.length < 2 281 | cmd_beef_target_help 282 | return 283 | end 284 | 285 | if args[2].nil? 286 | @remotebeef.settarget(val) 287 | cmds = @remotebeef.command.getcommands(@remotebeef.targetsession) 288 | tbl = Rex::Ui::Text::Table.new( 289 | 'Columns' => 290 | ['Command Id', 291 | 'Command', 292 | 'Execute Count' 293 | ]) 294 | cmds.each{ |x| 295 | x['children'].each { |y| 296 | tbl << [y['id'].to_s, 297 | x['text'].sub(/\W\(\d.*/,"")+"/"+y['text'].gsub(/[-\(\)]/,"").gsub(/\W+/,"_"), 298 | @remotebeef.command.getcmdexeccount(@remotebeef.targetsession,y['id'])] if @remotebeef.command.getcmdexeccount(@remotebeef.targetsession,y['id']) > 0 299 | } 300 | } 301 | puts "\n" 302 | puts "List of previous command modules for this target\n" 303 | puts tbl.to_s + "\n" 304 | return 305 | else 306 | @remotebeef.settarget(args[1]) 307 | @remotebeef.command.setmodule(args[2]) 308 | tbl = Rex::Ui::Text::Table.new( 309 | 'Columns' => 310 | [ 311 | 'Response Id', 312 | 'Executed Time', 313 | 'Response' 314 | ]) 315 | @remotebeef.command.getcmdresponses(@remotebeef.targetsession)['commands'].each do |resp| 316 | indiresp = @remotebeef.command.getindividualresponse(resp['object_id']) 317 | respout = "" 318 | if indiresp['results'].length == 0 or indiresp == nil 319 | respout = "No response yet" 320 | respdata = "" 321 | else 322 | respout = Time.at(indiresp['results'][0]['date'].to_i).to_s 323 | respdata = indiresp['results'][0]['data']['data'].to_s 324 | end 325 | tbl << [resp['object_id'].to_s,resp['creationdate'],respout] 326 | tbl << [respdata,"",""] 327 | end 328 | puts "\n" 329 | puts "List of responses for this command module\n" 330 | puts tbl.to_s + "\n" 331 | return 332 | end 333 | when "-c" 334 | if args[2].nil? 335 | @remotebeef.settarget(args[1]) 336 | cmds = @remotebeef.command.getcommands(@remotebeef.targetsession) 337 | tbl = Rex::Ui::Text::Table.new( 338 | 'Columns' => 339 | [ 340 | 'Id', 341 | 'Command', 342 | 'Execute Count' 343 | ]) 344 | cmds.each{ |x| 345 | x['children'].each{ |y| 346 | tbl << [y['id'].to_s, x['text'].sub(/\W\(\d.*/,"")+"/"+y['text'].gsub(/[-\(\)]/,"").gsub(/\W+/,"_"),@remotebeef.command.getcmdexeccount(@remotebeef.targetsession,y['id'])] 347 | } 348 | } 349 | puts "\n" 350 | puts "List of command modules for this target\n" 351 | puts tbl.to_s + "\n" 352 | else 353 | @remotebeef.settarget(args[1]) 354 | @remotebeef.command.setmodule(args[2]) 355 | print_line("Module name: " + @remotebeef.command.cmd['Name']) 356 | print_line("Module category: " + @remotebeef.command.cmd['Category']) 357 | print_line("Module description: " + @remotebeef.command.cmd['Description']) 358 | print_line("Module parameters:") 359 | 360 | @remotebeef.command.cmd['Data'].each{|data| 361 | print_line(data['name'] + " => " + data['value'] + " # this is the " + data['ui_label'] + " parameter") 362 | } if not @remotebeef.command.cmd['Data'].nil? 363 | end 364 | return 365 | when "-e" 366 | if args.length < 3 367 | cmd_beef_target_help 368 | return 369 | else 370 | @remotebeef.settarget(args[1]) 371 | @remotebeef.command.setmodule(args[2]) 372 | if not args[3].nil? 373 | #Therefore we have some parameters too #xntrik TODO - fix this ? 374 | pstring = "" 375 | (3..args.length-1).each do |x| 376 | pstring << args[x] << " " 377 | end 378 | pstring.chop! 379 | end 380 | @remotebeef.command.runmodule(@remotebeef.targetsession).nil? ? print_status("Command not sent") : print_status("Command sent") 381 | return 382 | end 383 | when "-h" 384 | cmd_beef_target_help 385 | return 386 | else 387 | cmd_beef_target_help 388 | return 389 | end 390 | } 391 | end 392 | 393 | def cmd_beef_target_help 394 | print_status("Use the \"target\" commands to interface with online, hooked browsers") 395 | print @@beef_target_opts.usage() 396 | end 397 | 398 | def cmd_beef_review(*args) 399 | if (args[0] == nil or args[0] == "-h") 400 | print_status("Listing offline browsers...") 401 | cmd_beef_offline 402 | cmd_beef_review_help 403 | return 404 | end 405 | 406 | if @remotebeef.session.connected.nil? 407 | print_status("You aren't connected") 408 | return 409 | end 410 | 411 | @@beef_review_opts.parse(args) {|opt, idx, val| 412 | case opt 413 | when "-i" 414 | if args.length < 2 415 | cmd_beef_review_help 416 | return 417 | end 418 | @remotebeef.setofflinetarget(val) 419 | info = @remotebeef.zombiepoll.getinfo(@remotebeef.targetsession) 420 | info['results'].each { |x| 421 | x['data'].each { |k,v| 422 | print_line(k+ " - "+v) 423 | } 424 | } 425 | when "-r" 426 | if args.length < 2 427 | cmd_beef_review_help 428 | return 429 | end 430 | if args[2].nil? 431 | @remotebeef.setofflinetarget(args[1]) 432 | cmds = @remotebeef.command.getcommands(@remotebeef.targetsession) 433 | tbl = Rex::Ui::Text::Table.new( 434 | 'Columns' => 435 | [ 436 | 'Command Id', 437 | 'Command', 438 | 'Execute Count' 439 | ]) 440 | cmds.each{ |x| 441 | x['children'].each{ |y| 442 | tbl << [y['id'].to_s, 443 | x['text'].sub(/\W\(\d.*/,"")+"/"+y['text'].gsub(/[-\(\)]/,"").gsub(/\W+/,"_"), 444 | @remotebeef.command.getcmdexeccount(@remotebeef.targetsession,y['id'])] if @remotebeef.command.getcmdexeccount(@remotebeef.targetsession,y['id']) > 0 445 | } 446 | } 447 | puts "\n" 448 | puts "List of previous command modules for this target\n" 449 | puts tbl.to_s + "\n" 450 | return 451 | else 452 | @remotebeef.setofflinetarget(args[1]) 453 | @remotebeef.command.setmodule(args[2]) 454 | tbl = Rex::Ui::Text::Table.new( 455 | 'Columns' => 456 | [ 457 | 'Response Id', 458 | 'Executed Time', 459 | 'Response' 460 | ]) 461 | @remotebeef.command.getcmdresponses(@remotebeef.targetsession)['commands'].each do |resp| 462 | indiresp = @remotebeef.command.getindividualresponse(resp['object_id']) 463 | respout = "" 464 | if indiresp['results'].length == 0 or indiresp == nil 465 | respout = "No response yet" 466 | respdata = "" 467 | else 468 | respout = Time.at(indiresp['results'][0]['date'].to_i).to_s 469 | respdata = indiresp['results'][0]['data']['data'].to_s 470 | end 471 | tbl << [resp['object_id'].to_s,resp['creationdate'],respout] 472 | tbl << [respdata,"",""] 473 | end 474 | end 475 | puts "\n" 476 | puts "List of responses for this command module\n" 477 | puts tbl.to_s + "\n" 478 | return 479 | when "-h" 480 | cmd_beef_review_help 481 | return 482 | else 483 | cmd_beef_review_help 484 | return 485 | end 486 | } 487 | end 488 | 489 | def cmd_beef_review_help 490 | print_status("Use the \"review\" commands to review previously hooked browsers, and commands executed against them") 491 | print @@beef_review_opts.usage() 492 | end 493 | end 494 | 495 | def initialize(framework, opts) 496 | 497 | super 498 | 499 | add_console_dispatcher(ConsoleCommandDispatcher) 500 | print_status("BeEF Bridge for Metasploit #{BeefVer}") 501 | print_good("Type %bldbeef_help%clr for a command listing") 502 | end 503 | 504 | def cleanup 505 | remove_console_dispatcher('Beef') 506 | end 507 | 508 | def name 509 | "beef" 510 | end 511 | 512 | def desc 513 | "BeEF Bridge for Metasploit #{BeefVer}" 514 | end 515 | protected 516 | end 517 | end --------------------------------------------------------------------------------