Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/parser/burp_session_document.rb
Views: 11623
# -*- coding: binary -*-1require "rex/parser/nokogiri_doc_mixin"23module Rex4module Parser56# If Nokogiri is available, define Burp Session document class.7#8# Burp Session XML files actually provide a lot, but since it also9# provides the originating url, we can pull most of the detail from10# the URI object.11load_nokogiri && class BurpSessionDocument < Nokogiri::XML::SAX::Document1213include NokogiriDocMixin1415# The resolver prefers your local /etc/hosts (or windows equiv), but will16# fall back to regular DNS. It retains a cache for the import to avoid17# spamming your network with DNS requests.18attr_reader :resolv_cache1920# Since we try to resolve every time we hit a new web page, need to21# hang on to our misses. Presume that it's a permanent enough failure22# that it won't get fixed during this particular import23attr_reader :missed_cache2425# If name resolution of the host fails out completely, you will not be26# able to import that Scan task. Other scan tasks in the same report27# should be unaffected.28attr_reader :parse_warning2930def start_document31@parse_warnings = []32@parse_warned = []33@resolv_cache = {}34@missed_cache = []35end3637def start_element(name=nil,attrs=[])38attrs = normalize_attrs(attrs)39block = @block40@state[:current_tag][name] = true41case name42when "host", "port", "protocol", "path"43@state[:has_text] = true44when "status"45@state[:has_text] = true46when "response"47@state[:has_text] = true48end49end5051def end_element(name=nil)52block = @block53case name54when "item" # Wrap up this item, but keep resolved web sites55collect_uri56report_web_site(&block)57handle_parse_warnings(&block)58report_web_page(&block)59report_web_service_info60report_web_host_info61# Reset the state once we close a host62@state = @state.select {|k| [:current_tag, :web_sites].include? k}63when "host"64@state[:has_text] = false65collect_host66@text = nil67when "port"68@state[:has_text] = false69collect_port70@text = nil71when "protocol"72@state[:has_text] = false73collect_protocol74@text = nil75when "path"76@state[:has_text] = false77collect_path_and_query78@text = nil79when "status"80@state[:has_text] = false81collect_status82@text = nil83when "response"84@state[:has_text] = false85collect_response86@text = nil87end88@state[:current_tag].delete name89end9091def collect_host92return unless in_item93return unless has_text94@state[:host] = @text95end9697def collect_port98return unless in_item99return unless has_text100return unless @text.to_i.to_s == @text.to_s101@state[:port] = @text.to_i102end103104def collect_protocol105return unless in_item106return unless has_text107@state[:protocol] = @text108end109110def collect_path_and_query111return unless in_item112return unless has_text113path,query = @text.split(/\?+/,2)114return unless path115if query116@state[:query] = "?#{query}" # Can be nil117end118if path =~ /https?:[\x5c\x2f][\x5c\x2f]+[^\x5c\x2f][^\x5c\x2f]+([^?]+)/n119real_path = "/#{$1}"120else121real_path = path122end123@state[:path] = real_path124end125126def collect_status127return unless in_item128return unless has_text129return unless @text.to_i.to_s == @text130@state[:status] = @text.to_i131end132133def collect_uri134return unless in_item135return unless @state[:host]136return unless @state[:port]137return unless @state[:protocol]138return unless @state[:path]139url = @state[:protocol].to_s140url << "://"141url << @state[:host].to_s142url << ":"143url << @state[:port].to_s144url << @state[:path]145if @state[:query]146url << "?"147url << @state[:query]148end149@state[:uri] = URI.parse(url) rescue nil150end151152def report_web_host_info153return unless @state[:web_site]154return unless @state[:uri].kind_of? URI::HTTP155return unless @state[:web_site].service.host.name.to_s.empty?156host_info = {:workspace => @args[:workspace]}157host_info[:address] = @state[:web_site].service.host.address158host_info[:name] = @state[:uri].host159db_report(:host, host_info)160end161162def report_web_service_info163return unless @state[:web_site]164return unless @state[:service_info]165return unless @state[:web_site].service.info.to_s.empty?166service_info = {}167service_info[:host] = @state[:web_site].service.host168service_info[:port] = @state[:web_site].service.port169service_info[:proto] = @state[:web_site].service.proto170service_info[:info] = @state[:service_info]171db_report(:service, service_info)172end173174def report_web_page(&block)175return unless @state[:uri].kind_of? URI::HTTP176return unless @state[:status]177return unless @state[:web_site]178return unless @state[:response_headers].kind_of? Hash179headers = {}180@state[:response_headers].each do |k,v|181headers[k.to_s.downcase] ||= []182headers[k.to_s.downcase] << v183end184if headers["server"].kind_of? Array185@state[:service_info] = headers["server"].first186end187return unless @state[:response_body]188web_page_info = {:workspace => @args[:workspace]}189web_page_info[:web_site] = @state[:web_site]190web_page_info[:code] = @state[:status]191web_page_info[:path] = @state[:uri].path192web_page_info[:headers] = headers193web_page_info[:body] = @state[:response_body]194web_page_info[:query] = @state[:uri].query195url = @state[:uri].to_s.gsub(/\?.*/,"")196db.emit(:web_page, url, &block) if block197db_report(:web_page, web_page_info)198end199200def report_web_site(&block)201return unless @state[:uri].kind_of? URI::HTTP202vhost = @state[:uri].host203web_site_info = {:workspace => @args[:workspace]}204web_site_info[:vhost] = vhost205address = resolve_vhost_address(@state[:uri])206return unless address207web_site_info[:host] = address208web_site_info[:port] = @state[:uri].port209web_site_info[:ssl] = @state[:uri].kind_of? URI::HTTPS210web_site_obj = db_report(:web_site, web_site_info)211return unless web_site_obj212@state[:web_sites] ||= []213url = "#{@state[:uri].scheme}://#{@state[:uri].host}:#{@state[:uri].port}"214unless @state[:web_sites].include? web_site_obj215db.emit(:web_site, url, &block)216@state[:web_sites] << web_site_obj217end218@state[:web_site] = web_site_obj219end220221def collect_response222return unless in_item223return unless has_text224response_text = @text.dup225response_header_text,response_body_text = response_text.split(/\r*\n\r*\n/n,2)226return unless response_header_text227response_header = Rex::Proto::Http::Packet::Header.new228response_header.from_s response_header_text229@state[:response_headers] = response_header230@state[:response_body] = response_body_text231end232233def in_item234return false unless in_tag("item")235return false unless in_tag("items")236return true237end238239def has_text240return false unless @text241return false if @text.strip.empty?242@text = @text.strip243end244245def handle_parse_warnings(&block)246return if @parse_warnings.empty?247return unless block248@parse_warnings.each_with_index do |pwarn,i|249unless @parse_warned.include? i250db.emit(:warning, pwarn, &block)251@parse_warned << i252end253end254end255256def resolve_address(host)257return @resolv_cache[host] if @resolv_cache[host]258return false if @missed_cache.include? host259address = Rex::Socket.resolv_to_dotted(host) rescue nil260@resolv_cache[host] = address261if address262block = @block263db.emit(:address, address, &block) if block264else265@missed_cache << host266end267return address268end269270# Alias this271def resolve_vhost_address(uri)272if uri.host273address = resolve_address(uri.host)274case address275when false276return false277when nil278@parse_warnings << "Could not resolve address for '#{uri.host}', skipping."279end280else281@parse_warnings << "Could not determine a host for this import."282end283address284end285286end287288end289end290291292293