#!/usr/bin/env ruby #Jonathan Voris #12/17/08 #CS6573 Pentesing #Ruby script for importing Burp Suite logs into the Metasploit WMAP database. #Based on a WebScarab session import script by spinbad. require 'sqlite3' puts("<<< WMAP Burp Log Importer >>>") if ARGV.length < 2 $stderr.puts("Usage: #{File.basename($0)} burplog sqlite3database [target] [startentry] [lastentry]") exit end puts() #Handle arguments burpLogName = ARGV[0] databaseName = ARGV[1] target = nil startEntry = nil lastEntry = nil if ARGV.length > 2 target = ARGV[2] end if ARGV.length > 3 startEntry = ARGV[3].to_i end if ARGV.length > 4 lastEntry = ARGV[4].to_i end if File.file?(burpLogName) == false then $stderr.puts("ERROR: Burp log not found: #{burpLogName}.") exit end if File.file?(databaseName) == false then $stderr.puts("ERROR: database file not found: #{databaseName}.") exit end #Regexs for extracting pertinent information from Burp log entries borderRegex = /======================================================/ #From http://geekswithblogs.net/nsthompson/archive/2006/07/20/RegularExpressionForTime.aspx timeRegex = /((1+[012]+)|([123456789]{1}))(((:|\s)[0-5]+[0-9]+))(((:|\s)[0-5]+[0-9]+))?(\s)?((a|A|p|P)(m|M))/ hostRegex = /(http|https)?:\/\/(\S+):(\d+)/ #From http://www.regular-expressions.info/examples.html ipAddrRegex = /\[(\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b)\]/ methodRegex = /(HEAD|GET|POST|PUT|DELETE|TRACE|OPTIONS|CONNECT) \/([^\?]*)\?*(\S*) / responseRegex = /^HTTP\/\d.\d (\d\d\d)/ #Open the database file database = SQLite3::Database.new(databaseName) #Prepare the query for use within the log processing loop dbQuery = database.prepare("INSERT INTO requests(host,port,ssl,meth,path,headers,query,body,respcode,resphead,response,created) VALUES(:host,:port,:ssl,:meth,:path,:headers,:query,:body,:respcode,:resphead,:response,:created)"); #Open the burp log file File.open(burpLogName, "r") do |burpLog| #Keep track of which log entry we're on entryNumber = 0 #Grab the first log line. Continue the processing loop while there are more log lines to read. logLine = burpLog.gets while (logLine != nil) if (((startEntry == nil) or (entryNumber >= startEntry)) and ((lastEntry == nil) or (entryNumber <= lastEntry))) puts "Reading log entry #{entryNumber}." end #Following a border, the first line in an entry should contain the request time, #protocol (ssl or not), port, and IP address while (logLine =~ timeRegex) == nil logLine = burpLog.gets end #Get the request time #Burp doesn't store the request date, only the time of day. We could use the response #time as the "created" time, but burp doesn't log responses for all requests. #For now, just use the time of day as a timestamp. time = timeRegex.match(logLine)[0] #Check that this line contains a host value (otherwise skip ahead until we find it) while (logLine =~ hostRegex) == nil logLine = burpLog.gets end #Grab the whole host value so we can output the URL host = hostRegex.match(logLine)[0] #Get protocol and port (also get the hostname for the target check) protocol = hostRegex.match(logLine)[1] if protocol == "https" ssl = true else ssl = false end hostName = hostRegex.match(logLine)[2] port = hostRegex.match(logLine)[3] #Check that this line has an IP address while (logLine =~ ipAddrRegex) == nil logLine = burpLog.gets end #Get IP address (Burp has already resolved the host name, so why should we?) ipAddr = ipAddrRegex.match(logLine)[1] #There should be another border before the headers while (logLine =~ borderRegex) == nil logLine = burpLog.gets end #The next line should contain the method, path and query string while (logLine =~ methodRegex) == nil logLine = burpLog.gets end #Get the method, path and query string method = methodRegex.match(logLine)[1] path = methodRegex.match(logLine)[2] query = methodRegex.match(logLine)[3] if (((startEntry == nil) or (entryNumber >= startEntry)) and ((lastEntry == nil) or (entryNumber <= lastEntry))) #Now that we have the host and path we can output the request URL if (query != "") puts "URL: #{host}/#{path}?#{query}" else puts "URL: #{host}/#{path}" end end #The following lines will be request headers until a blank line is encountered logLine = burpLog.gets requestHeaders = "" while (logLine.strip != "") requestHeaders += logLine logLine = burpLog.gets end #The following lines will be the request body until an entry border is encountered logLine = burpLog.gets requestBody = "" while (logLine =~ borderRegex) == nil requestBody += logLine logLine = burpLog.gets end #Initialize the response variables responseHeaders = "" responseBody = "" #This is the trickiest part of parsing the Burp log. The next item in the log could #be either the response to this request or the next request. We have to check the #next line to see which it is. logLine = burpLog.gets if (logLine =~ responseRegex) #If the next entry is a response, parse it for inclusion in the same database record as the request respCode = responseRegex.match(logLine)[1] #The following lines will be response headers until a blank line is encountered logLine = burpLog.gets while (logLine.strip != "") responseHeaders += logLine logLine = burpLog.gets end #The following lines will be the response body until an entry border is encountered logLine = burpLog.gets while (logLine =~ borderRegex) == nil responseBody += logLine logLine = burpLog.gets end end #Grab any empty lines that are present to prepare for the next entry logLine = burpLog.gets while ((logLine != nil) and (logLine.strip == "")) logLine = burpLog.gets end #All the pertinent information has been parsed - now we need to add it to the database #If a start and/or end entry has been specified, check whether this entry is within the provided #bounds before adding it to the database. if (((startEntry == nil) or (entryNumber >= startEntry)) and ((lastEntry == nil) or (entryNumber <= lastEntry))) #If a target has been provided, check whether this entry's host name or IP #matches the target before adding it to the database. if ((target != nil) and (hostName != target) and (ipAddr != target)) puts("Skipping this entry: neither the host name nor the IP address match the specified target.") else #set the values in the query dbQuery.bind_param("host", ipAddr) dbQuery.bind_param("port", port) dbQuery.bind_param("ssl", ssl) dbQuery.bind_param("meth", method) dbQuery.bind_param("path", path) dbQuery.bind_param("headers", requestHeaders) dbQuery.bind_param("query", query) dbQuery.bind_param("body", requestBody) dbQuery.bind_param("respcode", respCode) dbQuery.bind_param("resphead", responseHeaders) dbQuery.bind_param("response", responseBody) dbQuery.bind_param("created", time) #execute the query! dbQuery.execute() puts("Entry added to the database.") end end #Increment the entry number for the next log entry entryNumber = entryNumber + 1 end end dbQuery.close() database.close() puts() puts("Log importation complete.")