Skip to content

Commit

Permalink
handle delayed interactions as well
Browse files Browse the repository at this point in the history
  • Loading branch information
dnet committed Dec 12, 2021
1 parent 1b02bd2 commit 2f7939a
Showing 1 changed file with 55 additions and 7 deletions.
62 changes: 55 additions & 7 deletions src/main/kotlin/burp/BurpExtender.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,43 @@ package burp

import java.net.URL
import java.util.*
import java.util.concurrent.ConcurrentHashMap

const val NAME = "Log4Shell scanner"

class BurpExtender : IBurpExtender, IScannerCheck {
class BurpExtender : IBurpExtender, IScannerCheck, IExtensionStateListener {

private lateinit var callbacks: IBurpExtenderCallbacks
private lateinit var helpers: IExtensionHelpers
private lateinit var collaborator: IBurpCollaboratorClientContext

private val crontab: ConcurrentHashMap<String, Pair<IHttpRequestResponse, IntArray>> = ConcurrentHashMap()
private val thread: Thread = object : Thread() {
override fun run() {
try {
while (true) {
sleep(60 * 1000) // 60 seconds -- poll every minute
val interactions = collaborator.fetchAllCollaboratorInteractions().groupBy { it.getProperty("interaction_id") }
for (entry in interactions.entries) {
val payload = entry.key
val (hrr, poff) = crontab[payload] ?: continue
handleInteractions(hrr, poff, entry.value, sync = false).forEach(callbacks::addScanIssue)
}
}
} catch (ex: InterruptedException) {
return
}
}
}

override fun registerExtenderCallbacks(callbacks: IBurpExtenderCallbacks) {
this.callbacks = callbacks
helpers = callbacks.helpers
collaborator = callbacks.createBurpCollaboratorClientContext()

callbacks.setExtensionName(NAME)
callbacks.registerScannerCheck(this)
callbacks.registerExtensionStateListener(this)
}

override fun doPassiveScan(baseRequestResponse: IHttpRequestResponse?): MutableList<IScanIssue> =
Expand All @@ -47,18 +68,24 @@ class BurpExtender : IBurpExtender, IScannerCheck {
val request = insertionPoint!!.buildRequest(bytes)
val poff = insertionPoint.getPayloadOffsets(bytes)
val hrr = callbacks.makeHttpRequest(baseRequestResponse!!.httpService, request)
// TODO launch a thread to handle background events
return handleInteractions(hrr, poff, payload)
val interactions = handleInteractions(hrr, poff,
collaborator.fetchCollaboratorInteractionsFor(payload), sync = true)
crontab[payload] = Pair(hrr, poff)
synchronized(thread) {
if (!thread.isAlive) thread.start()
}
return interactions
}

private fun handleInteractions(hrr: IHttpRequestResponse, poff: IntArray, payload: String): MutableList<IScanIssue> {
val interactions = collaborator.fetchCollaboratorInteractionsFor(payload)
private fun handleInteractions(hrr: IHttpRequestResponse, poff: IntArray,
interactions: List<IBurpCollaboratorInteraction>,
sync: Boolean): MutableList<IScanIssue> {
if (interactions.isEmpty()) return Collections.emptyList()
val iri = helpers.analyzeRequest(hrr)
val markers = callbacks.applyMarkers(hrr, Collections.singletonList(poff), Collections.emptyList())
return Collections.singletonList(object : IScanIssue {
override fun getUrl(): URL = iri.url
override fun getIssueName(): String = "Log4Shell (CVE-2021-44228)"
override fun getIssueName(): String = "Log4Shell (CVE-2021-44228) - " + (if (sync) "synchronous" else "asynchronous")
override fun getIssueType(): Int = 0x08000000
override fun getSeverity(): String = "High"
override fun getConfidence(): String = "Tentative"
Expand All @@ -69,7 +96,13 @@ class BurpExtender : IBurpExtender, IScannerCheck {
override fun getHttpMessages(): Array<IHttpRequestResponse> = arrayOf(markers)
override fun getHttpService(): IHttpService = hrr.httpService
override fun getIssueDetail(): String {
val sb = StringBuilder("<p>The application interacted with the Collaborator server in response to a request with a Log4Shell payload</p><ul>")
val sb = StringBuilder("<p>The application interacted with the Collaborator server <b>")
if (sync) {
sb.append("in response to")
} else {
sb.append("some time after")
}
sb.append("</b> a request with a Log4Shell payload</p><ul>")
for (interaction in interactions) {
sb.append("<li><b>")
sb.append(interaction.getProperty("type"))
Expand All @@ -81,10 +114,25 @@ class BurpExtender : IBurpExtender, IScannerCheck {
}
sb.append("</ul><p>This means that the web service (or another node in the network) is affected by this vulnerability. ")
sb.append("However, actual exploitability might depend on an attacker-controllable LDAP being reachable over the network.</p>")
if (!sync) {
sb.append("<p>Since this interaction occurred <b>some time after the original request</b> (compare " +
"the <code>Date</code> header of the HTTP response vs. the interactions timestamps above), " +
"<b>the vulnerable code might be in another process/codebase or a completely different " +
"host</b> (e.g. centralized logging, SIEM). There might even be multiple instances of " +
"this vulnerability on different pieces of infrastructure given the nature of the bug.</p>")
}
return sb.toString()
}
})
}

override fun consolidateDuplicateIssues(existingIssue: IScanIssue?, newIssue: IScanIssue?): Int = 0 // TODO could be better

override fun extensionUnloaded() {
synchronized(thread) {
if (thread.isAlive) {
thread.interrupt()
}
}
}
}

0 comments on commit 2f7939a

Please sign in to comment.