Aller au contenu
Retour à la recherche en sécurité

Recherche en sécurité

Extensions Burp en Python

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:

Envoyez-nous un message

Seul votre courriel est requis. Choisissez un sujet, ajoutez une note, puis envoyez.

Incident en cours? Appelez la ligne 24/7 plutôt que d’attendre une réponse: +1 450 681-1681, poste 277