├── VERSION ├── test └── buby_test.rb ├── java ├── buby.jar └── src │ ├── burp │ ├── IMenuItemHandler.java │ ├── IScanQueueItem.java │ ├── IScanIssue.java │ ├── IHttpRequestResponse.java │ ├── IBurpExtender.java │ └── IBurpExtenderCallbacks.java │ └── BurpExtender.java ├── .gitignore ├── lib ├── buby │ ├── extends.rb │ └── extends │ │ ├── buby_array_wrapper.rb │ │ ├── scan_issue.rb │ │ └── http_request_response.rb └── buby.rb ├── samples ├── drb_sample_cli.rb ├── watch_scan.rb ├── verb_tamperer.rb ├── drb_buby.rb ├── menu_copy_req.rb ├── mechanize_burp.rb └── poc_generator.rb ├── Rakefile ├── buby.gemspec ├── History.txt ├── bin └── buby └── README.rdoc /VERSION: -------------------------------------------------------------------------------- 1 | 1.3.1 -------------------------------------------------------------------------------- /test/buby_test.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'buby' 3 | -------------------------------------------------------------------------------- /java/buby.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/courts/buby/master/java/buby.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw? 2 | .DS_Store 3 | coverage 4 | rdoc 5 | pkg 6 | *.gem 7 | *.class 8 | -------------------------------------------------------------------------------- /lib/buby/extends.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'buby/extends/buby_array_wrapper' 3 | require 'buby/extends/http_request_response' 4 | require 'buby/extends/scan_issue' 5 | -------------------------------------------------------------------------------- /samples/drb_sample_cli.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # notice... we're using MRI ruby here, not JRuby (but either will work) 3 | 4 | require 'drb' 5 | 6 | unless drb_uri = ARGV.shift 7 | STDERR.puts "Usage: #{File.basename $0} druby://:" 8 | exit 1 9 | end 10 | 11 | drb = DRbObject.new nil, drb_uri 12 | rsp=drb.make_http_request 'example.com', 80, false, "GET / HTTP/1.0\r\n\r\n" 13 | 14 | puts rsp 15 | -------------------------------------------------------------------------------- /samples/watch_scan.rb: -------------------------------------------------------------------------------- 1 | 2 | module WatchScan 3 | def evt_http_message(tool_name, is_request, message_info) 4 | super(tool_name, is_request, message_info) 5 | if tool_name == 'scanner' 6 | if is_request 7 | puts "#"*70, "# REQUEST: #{message_info.url.toString}", "#"*70 8 | puts message_info.req_str 9 | puts 10 | else 11 | puts "#"*70, "# RESPONSE: #{message_info.url.toString}", "#"*70 12 | puts message_info.rsp_str 13 | puts 14 | end 15 | end 16 | end 17 | 18 | def init_WatchScan 19 | puts "WatchScan module initialized" 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /samples/verb_tamperer.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jruby 2 | require 'rubygems' 3 | require 'buby' 4 | 5 | module VerbTamperer 6 | def evt_proxy_message(*param) 7 | msg_ref, is_req, rhost, rport, is_https, http_meth, url, 8 | resourceType, status, req_content_type, message, action = param 9 | 10 | if is_req and http_meth == "GET" 11 | message[0,3] = "PET" 12 | action[0] = Buby::ACTION_DONT_INTERCEPT 13 | 14 | return super(*param).dup 15 | else 16 | return super(*param) 17 | end 18 | end 19 | end 20 | 21 | if __FILE__ == $0 22 | $burp = Buby.new() 23 | $burp.extend(VerbTamperer) 24 | $burp.start_burp() 25 | end 26 | -------------------------------------------------------------------------------- /samples/drb_buby.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'rubygems' 3 | require 'buby' 4 | require 'drb' 5 | 6 | module DrbBuby 7 | attr_reader :drb_server 8 | 9 | def evt_register_callbacks(cb) 10 | super(cb) 11 | # cb.issueAlert("[DrbBuby] Service on: #{@drb_server.uri}") 12 | end 13 | 14 | def init_DrbBuby 15 | ## want to bind the DRb service on a specific socket? 16 | uri ='druby://127.0.0.1:9999' 17 | ## or let it choose one automatically: 18 | # uri = nil 19 | @drb_server = DRb.start_service uri, self 20 | puts "[DrbBuby] Service on: #{@drb_server.uri}" 21 | self.alert("[DrbBuby] Service on: #{@drb_server.uri}") 22 | end 23 | end 24 | 25 | if __FILE__ == $0 26 | $burp = Buby.new 27 | $burp.extend(DrbBuby) 28 | $burp.start_burp() 29 | $burp.init_DrbBuby 30 | end 31 | 32 | -------------------------------------------------------------------------------- /lib/buby/extends/buby_array_wrapper.rb: -------------------------------------------------------------------------------- 1 | 2 | class Buby 3 | class BubyArrayWrapper 4 | include Enumerable 5 | 6 | attr_reader :array_obj 7 | 8 | def initialize(obj) 9 | @array_obj = obj 10 | end 11 | 12 | def [](*args) 13 | if args.size == 1 and args.first.kind_of? Numeric 14 | self.array_obj[args[0]] 15 | else 16 | self.to_a(*args) 17 | end 18 | end 19 | 20 | def each 21 | self.array_obj.size.times do |idx| 22 | yield self.array_obj[idx] 23 | end 24 | end 25 | 26 | def size 27 | self.array_obj.size 28 | end 29 | alias length size 30 | 31 | def first 32 | return(self.array_obj[0]) if(self.size > 0) 33 | end 34 | 35 | def last 36 | return self.array_obj[self.size - 1] if(self.size > 0) 37 | end 38 | 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /lib/buby/extends/scan_issue.rb: -------------------------------------------------------------------------------- 1 | require 'uri' 2 | 3 | class Buby 4 | 5 | class ScanIssuesList < BubyArrayWrapper 6 | def initialize(obj) 7 | ScanIssueHelper.implant(obj[0]) if obj.size > 0 8 | super(obj) 9 | end 10 | 11 | end 12 | 13 | module ScanIssueHelper 14 | # Returns a Ruby URI object derived from the java.net.URL object 15 | def uri 16 | @uri ||= URI.parse url.to_s if not url.nil? 17 | end 18 | 19 | # one-shot method to implant ourselves onto a target object's class 20 | # interface in ruby. All later instances will also get 'us' for free! 21 | def self.implant(base) 22 | return if @implanted 23 | base.class.instance_eval { include(ScanIssueHelper) } 24 | @implanted = true 25 | end 26 | 27 | def http_messages 28 | HttpRequestResponseList.new( self.getHttpMessages() ) 29 | end 30 | alias messages http_messages 31 | alias messages http_messages 32 | 33 | def self.implanted? ; @implanted; end 34 | end 35 | end 36 | 37 | -------------------------------------------------------------------------------- /samples/menu_copy_req.rb: -------------------------------------------------------------------------------- 1 | module CopyRequest 2 | def copyRequest(req) 3 | req = case 4 | when req.is_a?(Numeric) 5 | # offset to match UI 6 | self.proxy_history[req-1].req_str 7 | when req.kind_of?(String) 8 | req 9 | when (req.respond_to?(:java_class) and req.java_class.to_s == "[B") 10 | String.from_java_bytes(req) 11 | when req.respond_to?(:req_str) 12 | req.req_str 13 | else 14 | warn "unknown request type... ducking" 15 | req 16 | end 17 | 18 | java.awt.Toolkit.getDefaultToolkit.getSystemClipboard.setContents(java.awt.datatransfer.StringSelection.new(req), nil) 19 | req 20 | end 21 | alias copy_request copyRequest 22 | 23 | def init_CopyRequest 24 | CopyRequestHandler.init_handler("Copy request(s)", self) 25 | end 26 | end 27 | 28 | module CopyRequestHandler 29 | class << self 30 | attr_accessor :_burp 31 | attr_reader :menuItemCaption 32 | end 33 | 34 | def self.init_handler(menuItemCaption, _burp = $burp) 35 | @menuItemCaption = menuItemCaption 36 | @_burp = _burp 37 | @_burp.registerMenuItem(menuItemCaption, self) 38 | end 39 | 40 | def self.menuItemClicked(menuItemCaption, messageInfo) 41 | messageInfo = Buby::HttpRequestResponseList.new(messageInfo).map{|x| x.req_str}.join("\r\n\r\n#{'='*50}\r\n\r\n") 42 | java.awt.Toolkit.getDefaultToolkit.getSystemClipboard.setContents(java.awt.datatransfer.StringSelection.new(messageInfo), nil) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /java/src/burp/IMenuItemHandler.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | /* 4 | * @(#)IMenuItemHandler.java 5 | * 6 | * Copyright PortSwigger Ltd. All rights reserved. 7 | * 8 | * This code may be used to extend the functionality of Burp Suite and Burp 9 | * Suite Professional, provided that this usage does not violate the 10 | * license terms for those products. 11 | */ 12 | 13 | /** 14 | * This interface is used by implementations of the IBurpExtender 15 | * interface to provide to Burp Suite a handler for one or more custom menu 16 | * items, which appear on the various context menus that are used throughout 17 | * Burp Suite to handle user-driven actions. 18 | * 19 | * Extensions which need to add custom menu items to Burp should provide an 20 | * implementation of this interface, and use the registerMenuItem 21 | * method of IBurpExtenderCallbacks to register each custom menu 22 | * item. 23 | */ 24 | 25 | public interface IMenuItemHandler 26 | { 27 | /** 28 | * This method is invoked by Burp Suite when the user clicks on a custom 29 | * menu item which the extension has registered with Burp. 30 | * 31 | * @param menuItemCaption The caption of the menu item which was clicked. 32 | * This parameter enables extensions to provide a single implementation 33 | * which handles multiple different menu items. 34 | * @param messageInfo Details of the HTTP message(s) for which the context 35 | * menu was displayed. 36 | */ 37 | public void menuItemClicked( 38 | String menuItemCaption, 39 | IHttpRequestResponse[] messageInfo); 40 | } 41 | -------------------------------------------------------------------------------- /samples/mechanize_burp.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jruby 2 | 3 | require 'rubygems' 4 | require 'buby' 5 | require 'mechanize' 6 | require 'rbkb/http' 7 | require 'irb' 8 | 9 | include Java 10 | 11 | # This Buby handler implementation simply keeps cookie state synched 12 | # for a Mechanize agent by intercepting all requests sent through the 13 | # Burp proxy. This lets you use Mechanize in tandem with your browser 14 | # through Burp without having to fuss around with cookies. 15 | module MechGlue 16 | attr_accessor :mech_agent 17 | 18 | def evt_proxy_message(*param) 19 | msg_ref, is_req, rhost, rport, is_https, http_meth, url, 20 | resourceType, status, req_content_type, message, action = param 21 | 22 | if (not is_req) and (message =~ /Set-Cookie/i) 23 | 24 | rsp = Rbkb::Http::Response.new(message, :ignore_content_length => true) 25 | 26 | # Get an uri object ready for mechanize 27 | uri = URI.parse(url) 28 | uri.scheme = (is_https)? "https" : "http" 29 | uri.host = rhost 30 | uri.port = rport 31 | 32 | # Grab cookies from headers: 33 | rsp.headers.get_header_value('Set-Cookie').each do |cookie| 34 | WWW::Mechanize::Cookie.parse(uri, cookie) do |c| 35 | @mech_agent.cookie_jar.add(uri, c) 36 | end 37 | end 38 | end 39 | return(message) 40 | end 41 | end 42 | 43 | 44 | if __FILE__ == $0 45 | $mech = WWW::Mechanize.new 46 | #$mech.set_proxy('localhost', '8080') 47 | 48 | $burp = Buby.new() 49 | $burp.extend(MechGlue) 50 | $burp.mech_agent = $mech 51 | $burp.start_burp 52 | 53 | puts "$burp is set to #{$burp.class}" 54 | puts "$mech is set to #{$mech.class}" 55 | IRB.start 56 | end 57 | 58 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | require 'rake/clean' 4 | 5 | begin 6 | require 'jeweler' 7 | Jeweler::Tasks.new do |gem| 8 | gem.name = "buby" 9 | gem.summary = %q{Buby is a mashup of JRuby with the popular commercial web security testing tool Burp Suite from PortSwigger} 10 | gem.description = %q{Buby is a mashup of JRuby with the popular commercial web security testing tool Burp Suite from PortSwigger. Burp is driven from and tied to JRuby with a Java extension using the BurpExtender API. This extension aims to add Ruby scriptability to Burp Suite with an interface comparable to the Burp's pure Java extension interface.} 11 | gem.email = "emonti@matasano.com, td@matasano.com" 12 | gem.homepage = "http://tduehr.github.com/buby" 13 | gem.authors = ["Eric Monti, tduehr"] 14 | gem.platform = "java" 15 | gem.test_files = ["test/buby_test.rb"] 16 | gem.require_paths << 'java' 17 | gem.rdoc_options = ["--main", "README.rdoc"] 18 | gem.extra_rdoc_files = ["History.txt", "README.rdoc", "bin/buby"] 19 | end 20 | Jeweler::GemcutterTasks.new 21 | rescue LoadError 22 | puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler" 23 | end 24 | 25 | require 'rake/testtask' 26 | Rake::TestTask.new(:test) do |test| 27 | test.libs << 'lib' << 'test' << 'java' 28 | test.pattern = 'test/**/*_test.rb' 29 | test.verbose = true 30 | end 31 | 32 | task :test => :check_dependencies 33 | 34 | task :default => :test 35 | 36 | require 'rake/rdoctask' 37 | Rake::RDocTask.new do |rdoc| 38 | if File.exist?('VERSION') 39 | version = File.read('VERSION') 40 | else 41 | version = "" 42 | end 43 | 44 | rdoc.rdoc_dir = 'rdoc' 45 | rdoc.title = "buby #{version}" 46 | rdoc.rdoc_files.include('README*') 47 | rdoc.rdoc_files.include('History.txt') 48 | rdoc.rdoc_files.include('bin/buby') 49 | rdoc.rdoc_files.include('lib/**/*.rb') 50 | end 51 | 52 | -------------------------------------------------------------------------------- /buby.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = %q{buby} 8 | s.version = "1.3.1" 9 | s.platform = %q{java} 10 | 11 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 12 | s.authors = [%q{Eric Monti, tduehr}] 13 | s.date = %q{2011-12-05} 14 | s.description = %q{Buby is a mashup of JRuby with the popular commercial web security testing tool Burp Suite from PortSwigger. Burp is driven from and tied to JRuby with a Java extension using the BurpExtender API. This extension aims to add Ruby scriptability to Burp Suite with an interface comparable to the Burp's pure Java extension interface.} 15 | s.email = %q{emonti@matasano.com, td@matasano.com} 16 | s.executables = [%q{buby}] 17 | s.extra_rdoc_files = [ 18 | "History.txt", 19 | "README.rdoc", 20 | "bin/buby" 21 | ] 22 | s.files = [ 23 | "History.txt", 24 | "README.rdoc", 25 | "Rakefile", 26 | "VERSION", 27 | "bin/buby", 28 | "buby.gemspec", 29 | "java/buby.jar", 30 | "java/src/BurpExtender.java", 31 | "java/src/burp/IBurpExtender.java", 32 | "java/src/burp/IBurpExtenderCallbacks.java", 33 | "java/src/burp/IHttpRequestResponse.java", 34 | "java/src/burp/IMenuItemHandler.java", 35 | "java/src/burp/IScanIssue.java", 36 | "java/src/burp/IScanQueueItem.java", 37 | "lib/buby.rb", 38 | "lib/buby/extends.rb", 39 | "lib/buby/extends/buby_array_wrapper.rb", 40 | "lib/buby/extends/http_request_response.rb", 41 | "lib/buby/extends/scan_issue.rb", 42 | "samples/drb_buby.rb", 43 | "samples/drb_sample_cli.rb", 44 | "samples/mechanize_burp.rb", 45 | "samples/menu_copy_req.rb", 46 | "samples/poc_generator.rb", 47 | "samples/verb_tamperer.rb", 48 | "samples/watch_scan.rb", 49 | "test/buby_test.rb" 50 | ] 51 | s.homepage = %q{http://tduehr.github.com/buby} 52 | s.rdoc_options = [%q{--main}, %q{README.rdoc}] 53 | s.require_paths = [%q{lib}, %q{java}, %q{java}] 54 | s.rubygems_version = %q{1.8.6} 55 | s.summary = %q{Buby is a mashup of JRuby with the popular commercial web security testing tool Burp Suite from PortSwigger} 56 | s.test_files = [%q{test/buby_test.rb}] 57 | 58 | if s.respond_to? :specification_version then 59 | s.specification_version = 3 60 | 61 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 62 | else 63 | end 64 | else 65 | end 66 | end 67 | 68 | -------------------------------------------------------------------------------- /java/src/burp/IScanQueueItem.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | /* 4 | * @(#)IScanQueueItem.java 5 | * 6 | * Copyright PortSwigger Ltd. All rights reserved. 7 | * 8 | * This code may be used to extend the functionality of Burp Suite and Burp 9 | * Suite Professional, provided that this usage does not violate the 10 | * license terms for those products. 11 | */ 12 | 13 | /** 14 | * This interface is used to allow extensions to access details of items in the 15 | * Burp Scanner active scan queue. 16 | */ 17 | 18 | public interface IScanQueueItem 19 | { 20 | /** 21 | * Returns a description of the status of the scan queue item. 22 | * 23 | * @return A description of the status of the scan queue item. 24 | */ 25 | String getStatus(); 26 | 27 | /** 28 | * Returns an indication of the percentage completed for the scan queue item. 29 | * 30 | * @return An indication of the percentage completed for the scan queue item. 31 | */ 32 | byte getPercentageComplete(); 33 | 34 | /** 35 | * Returns the number of requests that have been made for the scan queue item. 36 | * 37 | * @return The number of requests that have been made for the scan queue item. 38 | */ 39 | int getNumRequests(); 40 | 41 | /** 42 | * Returns the number of network errors that have occurred for the scan 43 | * queue item. 44 | * 45 | * @return The number of network errors that have occurred for the scan 46 | * queue item. 47 | */ 48 | int getNumErrors(); 49 | 50 | /** 51 | * Returns the number of attack insertion points being used for the scan 52 | * queue item. 53 | * 54 | * @return The number of attack insertion points being used for the scan 55 | * queue item. 56 | */ 57 | int getNumInsertionPoints(); 58 | 59 | /** 60 | * This method allows the scan queue item to be cancelled. 61 | */ 62 | void cancel(); 63 | 64 | /** 65 | * This method returns details of the issues generated for the scan queue item. 66 | * 67 | * Note that different items within the scan queue may contain duplicated 68 | * versions of the same issues - for example, if the same request has been 69 | * scanned multiple times. Duplicated issues are consolidated in the main view 70 | * of scan results. You can implementIBurpExtender.newScanIssue to get details 71 | * only of unique, newly discovered scan issues post-consolidation. 72 | * 73 | * @return Details of the issues generated for the scan queue item. 74 | */ 75 | IScanIssue[] getIssues(); 76 | } 77 | -------------------------------------------------------------------------------- /History.txt: -------------------------------------------------------------------------------- 1 | == 1.2.1 / 2011-01-27 2 | * Fixed burp -v to print version... somewhat 3 | 4 | == 1.2.0 / 2010-08-29 5 | * added menu item support from 1.3 6 | * added some additional processing constants from 1.3 7 | 8 | == 1.1.7 / 2009-12-29 9 | * fix evt_proxy_message_raw bridge to modify proxy messages (broken in 1.1.6) 10 | * switched from bones to jeweler for project mgmt 11 | 12 | == 1.1.6 / 2009-11-19 13 | * fix 14 | * poc_generator.rb example fixed to properly parse port in Host header 15 | * fix evt_proxy_message bridge to deal correctly with binary content data 16 | 17 | == 1.1.5 / 2009-10-15 18 | * enhancements 19 | * added support for exitSuite in burp v 1.2.17+ 20 | * added samples/poc_generator.rb 21 | 22 | == 1.1.4.1 / 2009-09-22 23 | * fix 24 | * Buby.harvest_cookies_from_history() was broken. 25 | It now implements select() block semantics and always returns an array. 26 | 27 | == 1.1.4 / 2009-09-14 28 | * enhancements 29 | * buby got implants! (har har) 30 | * Ruby wrapper classes added for proxy_history, site_map, and scan_issues 31 | * Extensions module for IHttpRequestResponse burp's class implementation 32 | * Extensions module for IScanIssue burp's class implementation 33 | * Added -s/--state, -r/--require, -e/--extend to buby cmd-line executable 34 | * Added -v/--version buby cmd-line flag 35 | * Modified samples for use as modules with -r/-e as well as run standalone 36 | * Added drb client and server sample 37 | 38 | == 1.1.3.1 / 2009-09-09 39 | * fix 40 | * fixed a typo in the String type-check for Buby.getParameters() 41 | 42 | == 1.1.3 / 2009-08-25 43 | * 1 enhancement 44 | * new convenience methods added for iterating and searching through 45 | proxy history, scan history, etc. 46 | * 1 fix 47 | * The gem now includes a buby.jar which should be usable with Java 1.5+ 48 | (previously the jar had been compiled only for Java 1.6) 49 | 50 | == 1.1.2 / 2009-08-20 51 | * 1 enhancement 52 | * Support added for the new getScanIssues extender method exposed in v1.2.15 53 | See http://releases.portswigger.net/2009/08/v1215.html 54 | 55 | == 1.1.1 / 2009-06-24 56 | * Fix 57 | * fixed getSiteMap callback front-end so that it takes the urlprefix argument 58 | 59 | == 1.1.0 / 2009-06-18 60 | * 1 major enhancement 61 | * Support added for the new Burp API features added in Burp 1.2.09. 62 | See http://releases.portswigger.net/2009/05/v1209.html 63 | * 1 minor enhancement 64 | * buby command-line tool exposes arguments for debugging and IRB 65 | 66 | == 1.0.2 / 2009-06-02 67 | * Enhancements 68 | * Added a sample illustrating synching cookies with Mechanize 69 | 70 | == 1.0.1 / 2009-05-10 71 | * Enhancements 72 | * Added some sugar to make swapping Burp event handlers easier. 73 | * Fixed documentation errors 74 | 75 | == 1.0.0 / 2009-05-08 76 | 77 | * 1 major enhancement 78 | * Birthday! 79 | -------------------------------------------------------------------------------- /lib/buby/extends/http_request_response.rb: -------------------------------------------------------------------------------- 1 | require 'uri' 2 | 3 | class Buby 4 | 5 | class HttpRequestResponseList < BubyArrayWrapper 6 | def initialize(obj) 7 | HttpRequestResponseHelper.implant(obj[0]) if obj.size > 0 8 | super(obj) 9 | end 10 | 11 | end 12 | 13 | 14 | module HttpRequestResponseHelper 15 | 16 | # returns the response as a Ruby String object - returns an empty string 17 | # if response is nil. 18 | def response_str 19 | return response().nil? ? "" : ::String.from_java_bytes(response()) 20 | end 21 | alias response_string response_str 22 | alias rsp_str response_str 23 | 24 | 25 | # returns an array of response headers split into header name and value. 26 | # For example: 27 | # [ 28 | # ["HTTP/1.1 301 Moved Permanently"], 29 | # ["Server", "Apache/1.3.41 ..."], 30 | # ... 31 | # ] 32 | def response_headers 33 | if headers=(@rsp_split ||= rsp_str.split(/\r?\n\r?\n/, 2))[0] 34 | @rsp_headers ||= headers.split(/\r?\n/).map {|h| h.split(/\s*:\s*/,2)} 35 | end 36 | end 37 | alias rsp_headers response_headers 38 | 39 | # Returns the message body of the response, minus headers 40 | def response_body 41 | (@rsp_split ||= rsp_str.split(/\r?\n\r?\n/, 2))[1] 42 | end 43 | alias rsp_body response_body 44 | 45 | 46 | # Returns the full request as a Ruby String - returns an empty string if 47 | # request is nil. 48 | def request_str 49 | return request().nil? ? "" : ::String.from_java_bytes(request()) 50 | end 51 | alias request_string request_str 52 | alias req_str request_str 53 | 54 | 55 | # Returns a split array of headers. Example: 56 | # [ 57 | # ["GET / HTTP/1.1"], 58 | # ["Host", "www.example.org"], 59 | # ["User-Agent", "Mozilla/5.0 (..."], 60 | # ... 61 | # ] 62 | def request_headers 63 | if headers=(@req_split ||= req_str.split(/\r?\n\r?\n/, 2))[0] 64 | @req_headers ||= headers.split(/\r?\n/).map {|h| h.split(/\s*:\s*/,2)} 65 | end 66 | end 67 | alias req_headers request_headers 68 | 69 | 70 | # Returns the request message body or an empty string if there is none. 71 | def request_body 72 | (@req_split ||= req_str.split(/\r?\n\r?\n/, 2))[1] 73 | end 74 | alias req_body request_body 75 | 76 | 77 | # Returns a Ruby URI object derived from the java.net.URL object 78 | def uri 79 | @uri ||= URI.parse url.to_s if not url.nil? 80 | end 81 | 82 | 83 | # one-shot method to implant ourselves onto a target object's class 84 | # interface in ruby. All later instances will also get 'us' for free! 85 | def self.implant(base) 86 | return if @implanted 87 | base.class.instance_eval { include(HttpRequestResponseHelper) } 88 | @implanted = true 89 | end 90 | 91 | 92 | def self.implanted? ; @implanted; end 93 | end 94 | 95 | end 96 | -------------------------------------------------------------------------------- /bin/buby: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jruby 2 | 3 | require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib buby])) 4 | require 'irb' 5 | require 'optparse' 6 | 7 | args = {} 8 | 9 | begin 10 | opts = OptionParser.new do |o| 11 | o.banner = "Usage: #{File.basename $0} [options]" 12 | 13 | o.on_tail("-h", "--help", "Show this help message") do 14 | raise opts.to_s 15 | end 16 | 17 | o.on("-i", "--interactive", "Start IRB") { args[:irb] = true } 18 | 19 | o.on("-d", "--debug", "Debug info") { args[:debug] = true } 20 | 21 | o.on("-B", "--load-burp=PATH", "Load Burp Jar from PATH") do |b| 22 | args[:load_burp] = b 23 | end 24 | 25 | o.on('-s', '--state=FILE', "Restore burp state file on startup") do |r| 26 | args[:restore] = r 27 | end 28 | 29 | o.on('-r', '--require=LIB', 30 | 'load a ruby lib (or jar) after Burp loads') do |i| 31 | (args[:requires] ||= []).push(i) 32 | end 33 | 34 | o.on('-e', '--extend=MOD', 35 | 'Extend Buby with a module (loaded via -r?)') do |m| 36 | (args[:extensions] ||= []).push(m) 37 | end 38 | 39 | o.on('-v', '--version', 'Prints version and exits.') do 40 | puts "#{File.basename $0} v#{Buby::VERSION}" 41 | exit 0 42 | end 43 | end 44 | 45 | opts.parse!(ARGV) 46 | 47 | if jar=args[:load_burp] 48 | raise "Load Burp Error: #{jar} did not provide burp.StartBurp" unless Buby.load_burp(jar) 49 | end 50 | 51 | raise "Load Burp Error: Specify a path to your burp.jar with -B" unless Buby.burp_loaded? 52 | 53 | rescue 54 | STDERR.puts $! 55 | exit 1 56 | end 57 | 58 | $DEBUG=true if args[:debug] 59 | 60 | $burp = Buby.start_burp() 61 | 62 | if libs=args[:requires] 63 | libs.each {|lib| STDERR.puts "Loading: #{lib.inspect}"; require(lib)} 64 | end 65 | 66 | def resolve_const(str) 67 | raise "can't resolve empty name #{str.inspect}" if str.empty? 68 | names = str.split('::') 69 | obj = ::Object 70 | names.each do |name| 71 | raise "#{name.inspect} is not defined" unless obj.const_defined?(name) 72 | obj = obj.const_get(name) 73 | end 74 | return obj if obj != ::Object 75 | end 76 | 77 | if mods=args[:extensions] 78 | mods.each do |mod| 79 | obj = resolve_const(mod) 80 | raise "#{obj.name} is not a module" unless obj.kind_of? Module 81 | STDERR.puts "Extending $burp with: #{obj.name}" 82 | $burp.extend(obj) 83 | if $burp.respond_to?(imeth=:"init_#{mod.split('::').last}") 84 | $burp.__send__ imeth 85 | end 86 | end 87 | end 88 | 89 | if f=args[:restore] 90 | raise "no such file #{f.inspect}" unless File.exists?(f) 91 | STDERR.puts "Restoring burp state from: #{f.inspect}" 92 | $burp.restore_state(f) 93 | end 94 | 95 | if args[:irb] 96 | # yucky hack... 97 | IRB.setup(nil) 98 | IRB.conf[:IRB_NAME] = File.basename($0, ".rb") 99 | module IRB 100 | class <