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.
1from burp import IBurpExtender
2from burp import IHttpListener
3
4class BurpExtender(IBurpExtender, IHttpListener):
5 def registerExtenderCallbacks(self, callbacks):
6 self.callbacks = callbacks
7 self.helpers = callbacks.getHelpers()
8 callbacks.setExtensionName("Handle Authentication Plugin")
9 callbacks.registerHttpListener(self)
10 self.session_token = NoneAloitetaan 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.
1def makeAuth(self):
2 try:
3 headers = [
4 "POST /login HTTP/1.1",
5 "Host: 127.0.0.1:5000",
6 "Content-Length: 17",
7 "Content-Type: application/x-www-form-urlencoded"
8 ]
9 body = "username=username"
10
11 auth_message = self.helpers.buildHttpMessage(headers, body)
12 host = "127.0.0.1"
13 port = 5000
14 use_https = False
15
16 resp = self.callbacks.makeHttpRequest(host, port, use_https, auth_message)
17 resp_info = self.helpers.analyzeResponse(resp)
18 cookie = resp_info.getHeaders()[7]
19 self.session_token = cookie.split("session=")[-1].split(";")[0]
20 except Exception as e:
21 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.
1 def updateCookies(self, headers):
2 new_headers = []
3 cookie_seen = False
4 for header in headers:
5 if "Cookie:" in header:
6 new_headers.append("Cookie: session={}".format(self.session_token))
7 cookie_seen = True
8 else:
9 new_headers.append(header)
10 if not cookie_seen:
11 new_headers.append("Cookie: session={}".format(self.session_token))
12 return new_headersTä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:
1 def processHttpMessage(self, tool_flag, is_request, message_info):
2 if is_request:
3 request = message_info.getRequest()
4 request_info = self.helpers.analyzeRequest(request)
5 # Jos kohde on oikea
6 if "127.0.0.1:5000" in request_info.getHeaders()[1]:
7 # jos sessio eväste on tallennettu
8 if self.session_token:
9 # päivitetään voimassa oleva sessio eväste pyyntöön
10 new_headers = self.updateCookies(request_info.getHeaders())
11 body = request[request_info.getBodyOffset():]
12 new_message = self.helpers.buildHttpMessage(new_headers, body)
13 message_info.setRequest(new_message)että HTTP-vastauksille:
1else:
2 response = message_info.getResponse()
3 response_info = self.helpers.analyzeResponse(response)
4 # haetaan status koodi ja jos se on 302, sekä
5 # siirto on tapahtumassa polkuun /, tiedetää
6 # että sessio on vanhentunut, koska vastaus vie meidät
7 # kirjautumissivulle
8
9 if int(response_info.getStatusCode()) == 302 and "Location: /" in response_info.getHeaders():
10 # Päivitetään kirjautumiseväste eli suoritetaan
11 # kirjautuminen
12 self.makeAuth()
13
14 # Toistetaan äsken epäonnistunut HTTP-pyyntö, jossa
15 # palvelin pyysi uudelleen kirjautumista
16 req = message_info.getRequest()
17 req_i = self.helpers.analyzeRequest(req)
18 new_headers = self.updateCookies(req_i.getHeaders())
19 body = req[req_i.getBodyOffset():]
20 new_message = self.helpers.buildHttpMessage(new_headers, body)
21 host = "127.0.0.1"
22 port = 5000
23 use_https = False
24 success_resp = self.callbacks.makeHttpRequest(host, port, use_https, new_message)
25 # Asetetaan uudelleen suoritetun HTTP-pyynnön vastaus
26 # uudelleenkirjautumista vaativan vastauksen tilalle
27 message_info.setResponse(success_resp)
28 returnEli 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ä:
1from burp import IBurpExtender
2from burp import IHttpListener
3
4class BurpExtender(IBurpExtender, IHttpListener):
5 def registerExtenderCallbacks(self, callbacks):
6 self.callbacks = callbacks
7 self.helpers = callbacks.getHelpers()
8 callbacks.setExtensionName("Handle Authentication Plugin")
9 callbacks.registerHttpListener(self)
10 self.session_token = None
11
12 def makeAuth(self):
13 try:
14 headers = [
15 "POST /login HTTP/1.1",
16 "Host: 127.0.0.1:5000",
17 "Content-Length: 17",
18 "Content-Type: application/x-www-form-urlencoded"
19 ]
20 body = "username=username"
21
22 auth_message = self.helpers.buildHttpMessage(headers, body)
23 host = "127.0.0.1"
24 port = 5000
25 use_https = False
26
27 resp = self.callbacks.makeHttpRequest(host, port, use_https, auth_message)
28 resp_info = self.helpers.analyzeResponse(resp)
29 cookie = resp_info.getHeaders()[7]
30 self.session_token = cookie.split("session=")[-1].split(";")[0]
31 except Exception as e:
32 print(e)
33
34 def updateCookies(self, headers):
35 new_headers = []
36 cookie_seen = False
37 for header in headers:
38 if "Cookie:" in header:
39 new_headers.append("Cookie: session={}".format(self.session_token))
40 cookie_seen = True
41 else:
42 new_headers.append(header)
43 if not cookie_seen:
44 new_headers.append("Cookie: session={}".format(self.session_token))
45 return new_headers
46
47 def processHttpMessage(self, tool_flag, is_request, message_info):
48 if is_request:
49 request = message_info.getRequest()
50 request_info = self.helpers.analyzeRequest(request)
51
52 if "127.0.0.1:5000" in request_info.getHeaders()[1]:
53 if self.session_token:
54 new_headers = self.updateCookies(request_info.getHeaders())
55 body = request[request_info.getBodyOffset():]
56 new_message = self.helpers.buildHttpMessage(new_headers, body)
57 message_info.setRequest(new_message)
58
59 else:
60 response = message_info.getResponse()
61 response_info = self.helpers.analyzeResponse(response)
62 if int(response_info.getStatusCode()) == 302 and "Location: /" in response_info.getHeaders():
63 # Session on expiroitunut
64 self.makeAuth() # Update session token
65
66 # Replay the failed request with new session token
67 req = message_info.getRequest()
68 req_i = self.helpers.analyzeRequest(req)
69 new_headers = self.updateCookies(req_i.getHeaders())
70 body = req[req_i.getBodyOffset():]
71 new_message = self.helpers.buildHttpMessage(new_headers, body)
72 host = "127.0.0.1"
73 port = 5000
74 use_https = False
75 success_resp = self.callbacks.makeHttpRequest(host, port, use_https, new_message)
76 message_info.setResponse(success_resp)
77 returnHakkeroinnin oppiminen alkaa tästä
Sadat interaktiiviset kurssit, virtuaalilabrat ja CTF-haasteet selaimessasi. Aloita ilmainen kokeilu ilman korttitietoja.