Tous les testeurs d’intrusion utilisent la suite Burp à un moment ou un autre, mais peu en exploitent tout le potentiel. L’une des fonctions les plus remarquables de Burp est la possibilité d’écrire son propre gestionnaire sur mesure.
Burp est écrit en Java et ses API d’extension exposent une interface Java, mais cela ne veut pas dire que nous, les testeurs, sommes limités à Java: merci au merveilleux monde de Jython! Comme son nom l’indique, Jython est une implémentation de Python en Java. Qu’est-ce que cela signifie?
Cela signifie qu’on peut faire ceci depuis du code Python:
from Burp import ISomeWidget
Créons donc une extension Burp avec Jython. Comme référence, voici la documentation des interfaces Burp: https://portswigger.net/burp/extender/api/burp/package-summary.html
Il faut d’abord télécharger la dernière version (j’utilise ici la version 2.5.3, depuis https://www.jython.org).
Dans le menu Burp Extender -> Options, indiquez le chemin du fichier JAR sous Python Environment:
Le code lui-même se charge par le menu Burp Extender -> Extensions, avec le bouton Add. Assurez-vous de sélectionner le type d’extension Python. Voici une capture d’écran de mon code chargé sous le nom extension.py:
Cliquez sur Next et Burp se chargera de votre code. S’il y a des erreurs, elles seront affichées.
Pour commencer, écrivons une extension qui se charge correctement, s’exécute sans erreur, écrit quelques messages dans la console pour montrer que tout fonctionne comme prévu et, à titre d’exercice, ajoute un en-tête HTTP personnalisé à chaque requête.
D’abord, la plomberie, qui sera essentiellement la même pour toutes les extensions:
from burp import IBurpExtender
from burp import IHttpListener
from datetime import datetime
class BurpExtender(IBurpExtender, IHttpListener):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName("Marios plugin")
callbacks.registerHttpListener(self)
return
IBurpExtender doit être implémentée par toutes les extensions, et les implémentations doivent s’appeler BurpExtender. Sinon, vous ne passerez pas l’étape du chargement de l’extension.
La deuxième pièce essentielle du casse-tête est l’interface IHttpListener: c’est elle qui donne à notre extension l’occasion de voir tout le trafic HTTP qui traverse le proxy Burp. Lorsque nous interceptons le trafic, nous pouvons le voir et aussi le modifier.
C’est l’appel IBurpExtenderCallbacks::registerHttpListener ci-dessus qui opère la magie. Cela fait, il ne reste qu’à mettre en place le rappel pour IHttpListener::processHttpMessage (voir https://portswigger.net/burp/extender/api/burp/IHttpListener.html).
# currentRequest est un IHttpRequestResponse
def processHttpMessage(self, toolFlag, messageIsRequest, currentRequest):
# ne traiter que les requêtes
if not messageIsRequest:
return
Les paramètres sont simples: toolFlag est une énumération indiquant de quel outil Burp provient le trafic. Je ne l’explique pas davantage ici, mais il peut prendre l’une des valeurs suivantes: https://portswigger.net/burp/extender/api/burp/IBurpExtenderCallbacks.html
Le paramètre que je vérifie est messageIsRequest, puisque les réponses ne m’intéressent pas. Je veux m’assurer que seules les requêtes sont manipulées par notre extension.
À ce stade, nous sommes dans une requête et prêts à l’ajuster à nos besoins. J’aime commencer par un peu de sortie:
requestInfo = self._helpers.analyzeRequest(currentRequest.getRequest()) # analyzeRequest() retourne un IRequestInfo
timestamp = datetime.now()
print "Intercepting message at:", timestamp.isoformat()
Nous recueillons maintenant les en-têtes HTTP dans une liste Python
headers = requestInfo.getHeaders()
newHeaders = list(headers)
newHeaders.append("Timestamp: " + timestamp.isoformat())
Remarquez qu’à ce stade, nous avons ajouté un en-tête HTTP Timestamp.
parameters = requestInfo.getParameters()
for parameter in parameters:
print "parameter:"
print parameter.getName()
if 'HEADER' in parameter.getName():
newHeaders.append("via: " + parameter.getValue())
testmario = self._helpers.removeParameter(currentRequest.getRequest(), parameter)
currentRequest.setRequest(testmario)
La logique est simple. On récupère les paramètres de l’URL. On les affiche. Si l’un d’eux s’appelle HEADER, on le retire, mais pas avant d’avoir ajouté dans la liste Python un nouvel en-tête HTTP nommé via, auquel on donne la valeur de ce paramètre d’URL. Par exemple:
?HEADER=batman
devient:
via: batman
Enfin, nous pouvons faire l’appel setRequest en précisant le nouveau message:
bodyBytes = currentRequest.getRequest()[requestInfo.getBodyOffset():]
bodyStr = self._helpers.bytesToString(bodyBytes)
newMsgBody = bodyStr + timestamp.isoformat()
newMessage = self._helpers.buildHttpMessage(newHeaders, newMsgBody)
print "Sending modified message:"
print self._helpers.bytesToString(newMessage)
currentRequest.setRequest(newMessage)
return
Notez l’usage de getRequest et setRequest (voir la documentation https://portswigger.net/burp/extender/api/burp/IHttpRequestResponse.html).
Ce code d’extension de base devrait être un bon point de départ pour créer vos propres extensions Burp et ouvrir de belles possibilités d’intégration. Pensez à lancer Burp avec plus de mémoire; pour ma part, j’utilise:
java -XX:MaxPermSize=1G -jar burpsuite_pro_v1.5.20.jar
Si nous exécutons cela et que nous naviguons vers une URL comme:
www.superconfigure.com?HEADER=bacon
l’onglet Output devrait afficher:
Intercepting message at: 2013-12-12T16:40:16.375999 parameter: HEADER Sending modified message: GET /?HEADER=bacon HTTP/1.1 Host: www.superconfigure.com Timestamp: 2013-12-12T16:40:16.375999 via: bacon Content-Length: 26
Les en-têtes en rouge ci-dessus sont ceux qu’ajoute notre extension. Voici ce que cela donne dans Burp:
