BurpSuite - Lisäosien rakentaminen
Lisäosan kehittäminen Extender API:lla: automaattinen istunnonhallinta osa 1
Lisäosan rekisteröinti
Tässä moduulissa ratkaistaan oikean maailman ongelma, eli käsitellään automaattisesti tilanteet, jossa testattava sovellus käyttää todella lyhyitä kirjautumisaikoja. Tyypillisesti tämä koituu ongelmaksi kun halutaan käyttää minkään näköistä automaatioita, suorittaa skannauksia tai käyttää työkaluja joiden suorittaminen kestää pidempään kuin mitä käyttäjä pysyy kirjautuneena.
from burp import IBurpExtender
from burp import IHttpListener
class BurpExtender(IBurpExtender, IHttpListener):
def registerExtenderCallbacks(self, callbacks):
self.callbacks = callbacks
self.helpers = callbacks.getHelpers()
callbacks.setExtensionName("Handle Authentication Plugin")
callbacks.registerHttpListener(self)
self.session_token = None
Aloitetaan jo tähän mennessä tutuksi tulleella lisäosan rekisteröinnillä. Lisäämme tähän vain uuden objektin nimeltä self.session_token. Tämä tulee käyttöön myöhemmin, mutta on käytännössä vain voimassa olevan sessioevästeen säilöntäpaikka.
Kirjautumisen ohjelmointi
Seuraavaksi tarvitsemme funktion, joka osaa suorittaa sovelluksessa olevan kirjautumisen ohjelmallisesti ja päivittää tästä saatavan, uuden sessioevästeen aiemmin määritettyyn session_token -variaabeliin.
def makeAuth(self):
try:
headers = [
"POST /login HTTP/1.1",
"Host: 127.0.0.1:5000",
"Content-Length: 17",
"Content-Type: application/x-www-form-urlencoded"
]
body = "username=username"
auth_message = self.helpers.buildHttpMessage(headers, body)
host = "127.0.0.1"
port = 5000
use_https = False
resp = self.callbacks.makeHttpRequest(host, port, use_https, auth_message)
resp_info = self.helpers.analyzeResponse(resp)
cookie = resp_info.getHeaders()[7]
self.session_token = cookie.split("session=")[-1].split(";")[0]
except Exception as e:
print(e)
Yllä olevassa makeAuth -funktiossa rakennamme käytännössä HTTP-pyynnön, jolla voimme kirjautua palveluun, sitten lähetämme tämän HTTP-pyynnön makeHttpRequest -funktiolla. Luemme tästä saapuvan vastauksen ja kaivamme siitä uuden kirjautumisevästeen, jonka palvelu loi meille kirjautumisen johdosta.
makeHttpRequest -funktio ottaa vastaan seuraavat parametrit:
- host - eli domain-nimi, johon pyyntö lähetetään
- port - eli portti, johon pyyntö lähetetään
- use_https - käytetään HTTPS yhteyttä
- auth_message - eli äsken rakennettu HTTP-pyyntö
Keksien päivittäminen otsakkeisiin
Seuraavaksi luomme toisen funktion, joka päivittää voimassa olevan kirjautumisevästeen sille annettuun otsakelistaan.
def updateCookies(self, headers):
new_headers = []
cookie_seen = False
for header in headers:
if "Cookie:" in header:
new_headers.append("Cookie: session={}".format(self.session_token))
cookie_seen = True
else:
new_headers.append(header)
if not cookie_seen:
new_headers.append("Cookie: session={}".format(self.session_token))
return new_headers
Tämän voi toki tehdä myös processHttpMessage -funktion sisällä, mutta tässä esimerkissä olemme erotelleet tämän omaksi funktioksi. Funktio palauttaa päivitetyn listan otsakkeita, jossa on voimassa oleva eväste.
HTTP-pyynnön ja -vastauksen käsittelyn rakentaminen
Lopuksi kirjoitamme vielä processHttpMessage -funktioon tarvittavan toimintalogiikan sekä HTTP-pyynnöille:
def processHttpMessage(self, tool_flag, is_request, message_info):
if is_request:
request = message_info.getRequest()
request_info = self.helpers.analyzeRequest(request)
# Jos kohde on oikea
if "127.0.0.1:5000" in request_info.getHeaders()[1]:
# jos sessio eväste on tallennettu
if self.session_token:
# päivitetään voimassa oleva sessio eväste pyyntöön
new_headers = self.updateCookies(request_info.getHeaders())
body = request[request_info.getBodyOffset():]
new_message = self.helpers.buildHttpMessage(new_headers, body)
message_info.setRequest(new_message)
että HTTP-vastauksille:
else:
response = message_info.getResponse()
response_info = self.helpers.analyzeResponse(response)
# haetaan status koodi ja jos se on 302, sekä
# siirto on tapahtumassa polkuun /, tiedetää
# että sessio on vanhentunut, koska vastaus vie meidät
# kirjautumissivulle
if int(response_info.getStatusCode()) == 302 and "Location: /" in response_info.getHeaders():
# Päivitetään kirjautumiseväste eli suoritetaan
# kirjautuminen
self.makeAuth()
# Toistetaan äsken epäonnistunut HTTP-pyyntö, jossa
# palvelin pyysi uudelleen kirjautumista
req = message_info.getRequest()
req_i = self.helpers.analyzeRequest(req)
new_headers = self.updateCookies(req_i.getHeaders())
body = req[req_i.getBodyOffset():]
new_message = self.helpers.buildHttpMessage(new_headers, body)
host = "127.0.0.1"
port = 5000
use_https = False
success_resp = self.callbacks.makeHttpRequest(host, port, use_https, new_message)
# Asetetaan uudelleen suoritetun HTTP-pyynnön vastaus
# uudelleenkirjautumista vaativan vastauksen tilalle
message_info.setResponse(success_resp)
return
Eli käytännössä koodimme reagoi tilanteisiin, jossa palvelin vastaa tietyllä tavalla, joka viittaa siihen että kirjautuminen on vanhentunut. Sitten suoritamme itse kirjautumisen ohjelmallisesti, tallennamme tästä saadun, uuden evästeen ja käytämme tätä tulevissa kutsuissa. Ainoa monimutkaistava tekijä on, että kun huomaamme, että kirjautuminen on vanhentunut, niin joudumme toistamaan sen alkuperäisen HTTP-pyynnön johon se palvelin alunperin vastasi, että sessio ei ole enää voimassa. Kun olemme sen suorittaneet uudella evästeellä, asetamme tämän vastauksen sitten uutta kirjautumista vaativan vastauksen tilalle. Täten mikään työkalu ei ole tietoinen siitä, että kesken suorituksen tehtiin lennosta uudelleen kirjautuminen, eikä täten mikään mene rikki.
Lopullinen koodi
Lopullinen koodi tulee näyttämään jotakuinkin tältä:
from burp import IBurpExtender
from burp import IHttpListener
class BurpExtender(IBurpExtender, IHttpListener):
def registerExtenderCallbacks(self, callbacks):
self.callbacks = callbacks
self.helpers = callbacks.getHelpers()
callbacks.setExtensionName("Handle Authentication Plugin")
callbacks.registerHttpListener(self)
self.session_token = None
def makeAuth(self):
try:
headers = [
"POST /login HTTP/1.1",
"Host: 127.0.0.1:5000",
"Content-Length: 17",
"Content-Type: application/x-www-form-urlencoded"
]
body = "username=username"
auth_message = self.helpers.buildHttpMessage(headers, body)
host = "127.0.0.1"
port = 5000
use_https = False
resp = self.callbacks.makeHttpRequest(host, port, use_https, auth_message)
resp_info = self.helpers.analyzeResponse(resp)
cookie = resp_info.getHeaders()[7]
self.session_token = cookie.split("session=")[-1].split(";")[0]
except Exception as e:
print(e)
def updateCookies(self, headers):
new_headers = []
cookie_seen = False
for header in headers:
if "Cookie:" in header:
new_headers.append("Cookie: session={}".format(self.session_token))
cookie_seen = True
else:
new_headers.append(header)
if not cookie_seen:
new_headers.append("Cookie: session={}".format(self.session_token))
return new_headers
def processHttpMessage(self, tool_flag, is_request, message_info):
if is_request:
request = message_info.getRequest()
request_info = self.helpers.analyzeRequest(request)
if "127.0.0.1:5000" in request_info.getHeaders()[1]:
if self.session_token:
new_headers = self.updateCookies(request_info.getHeaders())
body = request[request_info.getBodyOffset():]
new_message = self.helpers.buildHttpMessage(new_headers, body)
message_info.setRequest(new_message)
else:
response = message_info.getResponse()
response_info = self.helpers.analyzeResponse(response)
if int(response_info.getStatusCode()) == 302 and "Location: /" in response_info.getHeaders():
# Session on expiroitunut
self.makeAuth() # Update session token
# Replay the failed request with new session token
req = message_info.getRequest()
req_i = self.helpers.analyzeRequest(req)
new_headers = self.updateCookies(req_i.getHeaders())
body = req[req_i.getBodyOffset():]
new_message = self.helpers.buildHttpMessage(new_headers, body)
host = "127.0.0.1"
port = 5000
use_https = False
success_resp = self.callbacks.makeHttpRequest(host, port, use_https, new_message)
message_info.setResponse(success_resp)
return
Valmis ryhtymään eettiseksi hakkeriksi?
Aloita jo tänään.
Hakatemian jäsenenä saat rajoittamattoman pääsyn Hakatemian moduuleihin, harjoituksiin ja työkaluihin, sekä pääset discord-kanavalle jossa voit pyytää apua sekä ohjaajilta että muilta Hakatemian jäseniltä.