Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Burp Suite Extension to detect PHP Object Injection in WordPress Plugins (read the code comments for additional info)
java_import 'burp.IBurpExtender'
java_import 'burp.IScannerCheck'
java_import 'burp.IScanIssue'
require 'java'
java_import 'java.util.Arrays'
java_import 'java.util.ArrayList'
#
# You will need to download JRuby's Complete.jar file from http://jruby.org/download and configure Burp Extender with its path.
# You will also need to install the WordPress PHP Object Injection WordPress Plugin created by White Fir Design.
# Tip: Remove "PHP object injection has occurred." from the WordPress PHP Object Injection WordPress Plugin's description to not cause a false positive.
#
# Inspiration/idea and WordPress Plugin from https://www.pluginvulnerabilities.com/2017/07/24/wordpress-plugin-for-use-in-testing-for-php-object-injection/
# Burp Extension code from https://raw.githubusercontent.com/PortSwigger/example-scanner-checks/master/ruby/CustomScannerChecks.rb
#
GREP_STRING = 'PHP object injection has occurred.'
GREP_STRING_BYTES = GREP_STRING.bytes.to_a
INJ_TEST = 'O:20:"PHP_Object_Injection":0:{}'.bytes.to_a
INJ_ERROR = 'PHP object injection has occurred.'
INJ_ERROR_BYTES = INJ_ERROR.bytes.to_a
class BurpExtender
include IBurpExtender, IScannerCheck
#
# implement IBurpExtender
#
def registerExtenderCallbacks(callbacks)
# keep a reference to our callbacks object
@callbacks = callbacks
# obtain an extension helpers object
@helpers = callbacks.getHelpers
# set our extension name
callbacks.setExtensionName 'WordPress PHP Object Injection Check'
# register ourselves as a custom scanner check
callbacks.registerScannerCheck self
end
# helper method to search a response for occurrences of a literal match string
# and return a list of start/end offsets
def _get_matches(response, match)
matches = ArrayList.new
start = 0
while start < response.length
start = @helpers.indexOf(response, match, true, start, response.length)
break if start == -1
matches.add [start, start + match.length].to_java :int
start += match.length
end
return matches
end
#
# implement IScannerCheck
#
def doPassiveScan(baseRequestResponse)
# look for matches of our passive check grep string
matches = self._get_matches(baseRequestResponse.getResponse, GREP_STRING_BYTES)
return nil if matches.length == 0
# report the issue
issues = ArrayList.new
issues.add CustomScanIssue.new(
baseRequestResponse.getHttpService,
@helpers.analyzeRequest(baseRequestResponse).getUrl,
[@callbacks.applyMarkers(baseRequestResponse, nil, matches)],
'WordPress PHP Object Injection',
'Submitting the serialized string O:20:"PHP_Object_Injection":0:{} returned: ' + GREP_STRING,
'High').to_java IScanIssue
return issues
end
def doActiveScan(baseRequestResponse, insertionPoint)
# make a request containing our injection test in the insertion point
checkRequest = insertionPoint.buildRequest INJ_TEST
checkRequestResponse = @callbacks.makeHttpRequest(
baseRequestResponse.getHttpService, checkRequest)
# look for matches of our active check grep string
matches = self._get_matches(checkRequestResponse.getResponse, INJ_ERROR_BYTES)
return nil if matches.length == 0
# get the offsets of the payload within the request, for in-UI highlighting
requestHighlights = [insertionPoint.getPayloadOffsets(INJ_TEST)]
# report the issue
issues = ArrayList.new
issues.add CustomScanIssue.new(
baseRequestResponse.getHttpService,
@helpers.analyzeRequest(baseRequestResponse).getUrl,
[@callbacks.applyMarkers(checkRequestResponse, requestHighlights, matches)],
'WordPress PHP Object Injection',
'Submitting the serialized string O:20:"PHP_Object_Injection":0:{} returned: ' + INJ_ERROR,
'High').to_java IScanIssue
return issues
end
def consolidateDuplicateIssues(existingIssue, newIssue)
# This method is called when multiple issues are reported for the same URL
# path by the same extension-provided check. The value we return from this
# method determines how/whether Burp consolidates the multiple issues
# to prevent duplication
#
# Since the issue name is sufficient to identify our issues as different,
# if both issues have the same name, only report the existing issue
# otherwise report both issues
if existingIssue.getIssueName == newIssue.getIssueName
return -1
else
return 0
end
end
end
#
# class implementing IScanIssue to hold our custom scan issue details
#
class CustomScanIssue
include IScanIssue
def initialize(httpService, url, httpMessages, name, detail, severity)
@httpService = httpService
@url = url
@httpMessages = httpMessages
@name = name
@detail = detail
@severity = severity
end
def getUrl()
@url
end
def getIssueName()
@name
end
def getIssueType()
0
end
def getSeverity()
@severity
end
def getConfidence()
'Certain'
end
def getIssueBackground()
nil
end
def getRemediationBackground()
nil
end
def getIssueDetail()
@detail
end
def getRemediationDetail()
nil
end
def getHttpMessages()
@httpMessages
end
def getHttpService()
@httpService
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment