BurpSuite - Lisäosien rakentaminen

Session Handling Action - Istunnonhallinnan laajentaminen Burpilla lisäosien avulla

Keskitaso
30 min

Yksi Burpin Extender API:n keskeisistä osista on SessionHandlingAction-rajapinta, jota voidaan käyttää istunnon käsittelytoimintojen (session handling action) luomiseen, jotka laajentavat jo olemassa olevaa istunnon hallintaa BurpSuitessa. Näitä toimintoja voidaan määrittää suoritettavaksi tiettyinä kohtina HTTP-pyyntöjen käsittelyn aikana, mahdollistaen tehtävien automatisoinnin, kuten uudelleenautentikoinnin, tokenien (kuten CSRF) hallinnan tai mukautettujen otsakkeiden (header) lisäämisen.

Istunnon hallintatoiminnot integroituvat BurpSuiten pyyntöjen käsittelyprosessiin. Niitä voidaan määrittää Istunnon hallintasäännöissä (session handling rules), jotka määrittävät, miten ja milloin näitä toimintoja kutsutaan. Tämä mahdollistaa saumattoman integroinnin työkalujen, kuten Proxy, Repeater, Intruder ja Scanner, kanssa.

Mukautetut istunnon hallintatoiminnot ovat hyödyllisiä tilanteissa, kuten:

  • Automatisoitu uudelleenautentikointi: Kun istunto vanhenee, laajennus voi kirjautua automaattisesti takaisin sisään.
  • Tokenien yms. hallinta: Toksujen (kuten vaikka CSRF-tokenit) erottaminen aiemmista vastauksista ja niiden lisääminen seuraaviin pyyntöihin.
  • Monimutkaiset tunnistautumis-flow:t: Burpin makroilla ei pääse kovin pitkälle jos tunnistautumis-flow:t ovat monimutkaisia.
  • Dynaaminen pyynnön muokkaus: Pyyntöjen muokkaaminen tiettyjen kriteerien tai tilan perusteella.

Tässä moduulissa näytämme, miten integroida mukautettu istunnonkäsittelylogiikka BurpSuiteen käyttäen Jythonia. Käymme läpi Jython-laajennuksen luomisen, joka istunnon vanhennuttua kirjautuu automaattisesti uudestaan web-sovellukseen lähettämällä POST-pyynnön hankalahkoon /login endpointtiin.

Ratkaistavana oleva ongelma

Jos haluat seurata mukana jo nyt, voit käynnistää labran tämän sivun alaosasta.

Okei, ensisilmäyksellä kirjautumislomake vaikuttaa varsin yksinkertaiselta.

Kirjaudumme sisään, rastitamme ruudun, meidät kirjataan ulos. Okei.

Hermot ei kestä tehdä tätä manuaalisesti 100 kertaa. Haluamme, että Burp pitää meidät kirjautuneena sisään samalla kun Intruder lähettää 100 pyyntöä kutakin ruutua kohti niin, että lisäämme vain numeroa X-akselille ja Y-akselille.

Okei, tarvitsemme siis session handling -makron lähettämään kirjautumispyynnön. Sehän on helppoa. Katsotaanpa pyyntöä.

HTTP-Pyyntö
POST /login HTTP/2
Host: www-5xf7ztxfqo.ha-target.com
Cookie: HakatemiaLabSessionAffinity=f4f5761d0a3cdae05aee677ec9e940c3|766d492e152f0eb48bca4df922227d80; session=721b2d8f-e5d3-4d19-9417-865472817ab6
...

username=john.doe%40example.com&password=s3cr3t&timestamp=1732040519&checksum=9f76872042fb7bae07c1c85d8ee7fc6d

Okei... Näyttää hieman huolestuttavalta... Aikaleima? Tarkistussumma? Katsotaan HTML-lähdekoodia nähdäksemme, mitä täällä tapahtuu. Napsauta hiiren kakkospainikkeella, "Näytä lähdekoodi"...

<body>
    <h1>Login</h1>
    <p>Username is: john.doe@example.com</p>
    <p>Password is: s3cr3t</p>
    <form method="post" action="/login" onsubmit="return addHashData(this)">
      <label for="username">Username:</label>
      <input type="text" id="username" name="username" required />
      <label for="password">Password:</label>
      <input type="password" id="password" name="password" required />
      
      <input type="hidden" name="timestamp" id="timestamp" />
      <input type="hidden" name="checksum" id="checksum" />
      
      <button type="submit">Login</button>
    </form>

    
    <script>
      function addHashData(form) {
        const timestamp = Math.floor(Date.now() / 1000);
        const username = form.username.value;
        const password = form.password.value;
        
        form.timestamp.value = timestamp;
        const dataToHash = username + password + timestamp;
        form.checksum.value = CryptoJS.MD5(dataToHash).toString();
        
        return true;
      }
    </script>
    
  </body>

Okei, joten nyt voimme nähdä, miten nuo arvot lasketaan. Aikaleima on epoch (eli unix-aikaleima, eli kuluneet sekunnit vuoden 1970 alusta). Ja tarkistussumma on MD5-tarkistussumma kaikista kolmesta konkatenoituna seuraavassa järjestyksessä: käyttäjänimi, salasana, aikaleima.

Vaikuttaa siltä, että makro ei tällä kertaa riitä, meidän täytyy kääriä hihat ja kirjoittaa laajennus.

Pohjan luominen laajennukseen ja sen lataaminen burppiin

Oletan, että olet seurannut aiemmin kurssilla kuvattua kehitysympäristön määritystä. Jos et ole, aloita siitä.

Luo tiedosto, kuten auto_login.py, seuraavalla pohjalla:

from burp import IBurpExtender, ISessionHandlingAction, IHttpRequestResponse

class BurpExtender(IBurpExtender, ISessionHandlingAction):
    def registerExtenderCallbacks(self, callbacks):
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()
        callbacks.setExtensionName("Auto Login")
        callbacks.registerSessionHandlingAction(self)

    def performAction(self, currentRequest, macroItems):  # type: (IHttpRequestResponse, list[IHttpRequestResponse]) -> None
        print("Performing action here!")

    def getActionName(self):
        return "Auto Login"

Tämä on hyvä alku mille tahansa session handling action -laajennukselle, saatat haluta bookmarkata tämän sivun, jotta voit aina kopioida sen tarvittaessa.

Lataamme laajennuksen Burppiin nyt.

Kuten kuvakaappauksesta voidaan nähdä, meillä on nyt yksi istunnon käsittelytoiminto ladattuna Burppiin. Nyt selvitetään, miten ja milloin sitä käytetään.

Istunnon käsittelytoimintojen määritys

Toivon, että tämä ei ole ensimmäinen kerta kun näet tämän "ruutujen rastitus" -harjoituksen, vaan että olet suorittanut BurpSuite-peruskurssin ja tiedät jo istunnon käsittelytoiminnoista, mitä ne ovat ja miten ne toimivat. Jos et ole, palaa nyt takaisin tai sinulla tulee todennäköisesti vaikeuksia jatkaa eteenpäin. Emme toista kaikkea tässä.

Asetetaan seuraavaksi session handling -rulet.

Kuinka havaitsemme vanhentuneet istunnot?

Ensinnäkin tutkitaan havaitsemismekanismi, kun istunto ei enää ole kelvollinen. Lähetä tämä viimeisin HTTP-pyyntö toistimeen ja lähetä se varmistaen, että vastaus on edelleen 200 OK.

Nyt kirjaudu ulos sovelluksesta (mikä yleensä vanhentaa istunnon) ja lähetä sama pyyntö uudelleen. Katsotaan, mitä tapahtuu.

Vastaus ei enää ole 200 vaan sisältää uudelleenohjauksen kirjautumissivulle Sijainti-otsakkeessa. Uudelleenohjaaminen kirjautumissivulle on usein erinomainen tapa tunnistaa vanhentunut istunto.

Sääntöjen luominen

Avaa Burpin asetukset ja siirry kohtaan "Istunnot".

Evästeiden säiliön määritys

Kuten olet (toivottavasti) jo oppinut, Burp voi automaattisesti päivittää evästeiden luetteloa HTTP-vastauksista, jotka havaitaan eri työkaluissa. Emme kuitenkaan halua tämän tapahtuvan hallitsemattomasti, muuten esimerkiksi sovelluksen selailu Burpin proxyn kautta saattaa häiritä käynnissä olevaa skannausta.

Joten, jos "Proxy" on valittu, poista se.

Nyt meillä on istuntotietokanta valmiina.

Oletussääntöjen poistaminen

Poistetaan oletussäännöstö kokonaan ja luodaan uusi. Klikkaa siis kohtaa "Poista", jotta luettelo tyhjenee.

Luo uusi sääntö

Luo uusi sääntö käyttäen "Lisää" painiketta. Anna sille kuvaus, kuten "Omat istunnon käsittelysäännöt".

Määritä kohdealue

Sitten avaa "Kohdealue" välilehti ja lisää "Toistin"- ja "Hyökkääjä" -työkalut kohdealueeseen.

Voit valita URL-alueelle "Käytä Burpin scopea". Huomaa kuitenkin, että sinun tulee lisätä kohde-URL-osoite scopeen kuten aiemmin kurssilla keskusteltiin.

Lisää sääntö "Käytä evästeitä istunnon käsittelyevästeiden säiliöstä". Tämä päivittää aina HTTP-pyynnön evästeet kohteessa olevasta Burpin evästesäiliöstä.

Voit tällä vaiheella rajoittaa, mitkä evästeet haluat päivittää. Voit jättää nämä tyhjäksi ja kaikki evästeet päivitetään.

Nyt meillä on myös päivitysmekanismi liitetty. Tarvitsemme vielä havaitsemismekanismin ja todennusmekanismin.

Toinen sääntö - Istunnon tarkistus

Lisää sääntö "Tarkista, että istunto on kelvollinen". Tässä laatikossa on hieman hienosäätöä tehtävänä, mennään palanen kerrallaan.

Tee pyyntö(jä) istunnon varmistamiseksi: Ensimmäinen vaihe on:

  • Käsittele nykyinen pyyntö: Määritä istunnon tila nykyisen HTTP-pyynnön HTTP-vasteesta (sen sijaan, että lähetettäisiin täysin erillinen HTTP-pyyntö. Toisinaan tämä saattaa olla tarpeen, mutta ei nyt).
  • Tarkistetaan tila jokaisen HTTP-pyynnön kohdalla: Eli jätä ruutu valitsematta kohta "Tarkista istunto vain joka N:s pyyntö".

Kohdassa "Tarkista vastaus istunnon kelvollisuuden määrittämiseksi" Valitse:

  • Location: Uudelleenohjauksen kohdesivun URL eli meille tärkeä tieto sijaitsee HTTP-vastauksen Location-otsakkeessa.
  • Match type: Literal string (tavan tekstiä, ei siis regex-patterni).
  • Case-sensitivity: Insensitive (ei ole väliä onko isoja vai pieniä kirjaimia).
  • Match indicates: Virheellisen istunnon, mikä tarkoittaa, että jos vastauksessa on vastaava osuma kuin kuvattu, se tarkoittaa, että istunto ei ole kelvollinen. Tämä voitaisiin tehdä myös päinvastoin, jos se on helpompaa kyseiselle sovellukselle.

Määritä käyttäytyminen istunnon kelvollisuudessa: Nyt käskemme Burpin käyttää istunnon käsittelytoimintoa laajennuksestamme.

  • Jos istunto ei kelpaa, suorita alla oleva toiminto (Jos istunto ei ole kelvollinen, tee seuraava):
  • Suorita makro (Suorita makro)

Mutta hetkinen, emmehän me halunneet käyttää makroa. Aivan, emme haluakaan. Mutta Burp ei anna meidän käyttää istunnon käsittelytoimintoa (laajennusta) "tarkista, että istunto on kelvollinen" -käsittelijässä, ellemme suorita ensin jotain makroa. Burppi on burppi, minkäs sille tekee, mutta voimme kiertää tämän luomalla tyhjän makron ilman pyyntöjä.

Ja nyt taikasana - rastitaan "Makron suorituksen jälkeen, kutsu Burp-laajennuksen toimintakäsittelijä" ja valitaan laajennuksemme.

Paina "OK" ja olemme valmiita testaamaan laajennusta. Sitä ennen lisää kuitenkin yksi "Käytä evästeitä Burpin evästeiden säiliöstä" -sääntöjen loppuun - jotta istunnon käsittelytoiminnon päivittämät evästeet käytetään tehtävässä pyynnössä. Tulisi näyttää tältä:

Asetuksen testaaminen Session Tracer -työkalulla

Napsauta "Open sessions tracer".

Lähetä "ruutujen rastitus" -viesti Kaappaajalla. Tarkista sitten session tracer. Sinun tulisi nähdä, että se on yrittänyt palauttaa istunnon käyttäen laajennustamme, mutta tietenkään se ei vielä tee mitään.

Voimme myös siirtyä laajennuksen tulosteeseen ja havainnoida, että performAction -funktiota on todella kutsuttu ja viestimme on tulostettu.

Suorita toiminto -metodin toteuttaminen

Nyt toteutetaan performAction-metodi lähettämään kirjautumispyyntö.

Tarvittavat importit

Tarvitsemme time-moduulin aikaleiman hakemiseen ja hashlib-moduulin MD5-tarkistussumman luomiseen. ICookie on tarpeen, jotta voimme laajentaa ICookie-rajapintaa ja luoda oman Cookie-luokkamme, jotta voimme päivittää evästeen keksipurkkiin. Jep. Älä syytä minua, en koodannut Burpia tai sen API:a.

import time
import hashlib
from burp import ICookie

Käyttäjätunnusten ja kohde-URL-osoitteen määritys

Voimme kovakoodata käyttäjätunnukset ja URL-osoitteen yksinkertaisuuden vuoksi.

USERNAME = "john.doe@example.com"
PASSWORD = "s3cr3t"
LOGIN_URL = "https://www-sinun-labra.ha-target.com/login"

Tarkistussumman laskeminen

Voimme saada aikaleiman muuntamalla ensin time.time() kokonaisluvuksi (päästäksemme eroon desimaaleista) ja sitten muutamme sen merkkijonoksi.

Sitten yhdistämme käyttäjänimen, salasanan ja aikaleiman yhteen.

Ja lopuksi rakennamme tästä merkkijonosta MD5-tarkistussumman.

timestamp = str(int(time.time()))
checksum_input = USERNAME + PASSWORD + timestamp
checksum = hashlib.md5(checksum_input.encode('utf-8')).hexdigest()

Kirjautumispyynnön lähettäminen

HTTP-pyynnön lähettäminen Burp-laajennuksen avulla on valitettavasti hieman monisanaisempaa kuin esimerkiksi requests-kirjaston käyttö. Mutta pysyhän mukana.

# Parse the login URL
parsed_url = URL(LOGIN_URL)
protocol = parsed_url.getProtocol()
host = parsed_url.getHost()
port = parsed_url.getPort()
if port == -1:
    port = 443 if protocol == "https" else 80
is_https = protocol == "https"

# Build the HTTP service
login_service = self._helpers.buildHttpService(host, port, is_https)

# Create the POST request headers
request_headers = [
    "POST {} HTTP/1.1".format(parsed_url.getPath()),
    "Host: {}".format(host),
    "Content-Type: application/x-www-form-urlencoded",
    "Connection: close"
]

# Build the request body with URL-encoded parameters
request_body = "username={}&password={}&timestamp={}&checksum={}".format(
    self._helpers.urlEncode(USERNAME),
    self._helpers.urlEncode(PASSWORD),
    self._helpers.urlEncode(timestamp),
    self._helpers.urlEncode(checksum)
)

# Combine headers and body into a complete HTTP message
message = self._helpers.buildHttpMessage(request_headers, request_body)

response = self._callbacks.makeHttpRequest(login_service, message)

Kirjautumisvastauksen analysointi

Nyt kun pyyntö on lähetetty ja vastaus on saatu, meidän on napattava evästeet vastauksesta.

response_bytes = response.getResponse()
if response_bytes:
    response_info = self._helpers.analyzeResponse(response_bytes)
    cookies = response_info.getCookies()

Evästesäilön päivittäminen

Viimeiseksi, kun meillä on evästeet, lisätään ne säiliöön. Tämän luulisi olevan helppoa, mutta eipäs olekaan.

Ensin meidän täytyy määrittää oma Cookie-luokka jossakin (esim. ihan ylhäällä class BurpExtender -yläpuolella).

class Cookie(ICookie):

    def getDomain(self):
        return self.cookie_domain

    def getPath(self):
        return self.cookie_path

    def getExpiration(self):
        return self.cookie_expiration

    def getName(self):
        return self.cookie_name

    def getValue(self):
        return self.cookie_value

    def __init__(self, cookie_domain=None, cookie_name=None, cookie_value=None, cookie_path=None,
                 cookie_expiration=None):
        self.cookie_domain = cookie_domain
        self.cookie_name = cookie_name
        self.cookie_value = cookie_value
        self.cookie_path = cookie_path
        self.cookie_expiration = cookie_expiration

Sitten käymme läpi vastauksen evästeet, luomme uusia Cookie-objekteja niiden perusteella ja päivitämme sitten evästesäilön.

for cookie in cookies:
    # Ensure the domain is set
    domain = cookie.getDomain()
    if domain is None:
        domain = host  # Set the domain to the request host

    # Create a new Cookie object with the domain set
    new_cookie = Cookie(
        domain,
        cookie.getName(),
        cookie.getValue(),
        cookie.getPath(),
        cookie.getExpiration()
    )
    self._callbacks.updateCookieJar(new_cookie)

Kootaan kaikki yhteen

Tässä on valmis laajennuksemme! Olen lisännyt siihen kommentteja, jotta debuggaaminen olisi helpompaa (voit kurkkailla logeja extensions -välilehdeltä).

from burp import IBurpExtender, ISessionHandlingAction
from burp import ICookie
import time
import hashlib
from java.net import URL

USERNAME = "john.doe@example.com"
PASSWORD = "s3cr3t"
LOGIN_URL = "https://www-5xf7ztxfqo.ha-target.com/login"


class Cookie(ICookie):

    def getDomain(self):
        return self.cookie_domain

    def getPath(self):
        return self.cookie_path

    def getExpiration(self):
        return self.cookie_expiration

    def getName(self):
        return self.cookie_name

    def getValue(self):
        return self.cookie_value

    def __init__(self, cookie_domain=None, cookie_name=None, cookie_value=None, cookie_path=None,
                 cookie_expiration=None):
        self.cookie_domain = cookie_domain
        self.cookie_name = cookie_name
        self.cookie_value = cookie_value
        self.cookie_path = cookie_path
        self.cookie_expiration = cookie_expiration


class BurpExtender(IBurpExtender, ISessionHandlingAction):

    def registerExtenderCallbacks(self, callbacks):
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()

        callbacks.setExtensionName("Auto Login")
        callbacks.registerSessionHandlingAction(self)

    def getActionName(self):
        return "Auto Login"

    def performAction(self, currentRequest, macroItems):
        try:
            print("[Auto Login] Starting performAction")

            # Get the current epoch time as an integer string
            timestamp = str(int(time.time()))
            print("[Auto Login] Current timestamp:", timestamp)

            # Compute the checksum (MD5 hash)
            checksum_input = USERNAME + PASSWORD + timestamp
            checksum = hashlib.md5(checksum_input.encode('utf-8')).hexdigest()
            print("[Auto Login] Checksum input:", checksum_input)
            print("[Auto Login] Computed checksum:", checksum)

            # Parse the login URL
            parsed_url = URL(LOGIN_URL)
            protocol = parsed_url.getProtocol()
            host = parsed_url.getHost()
            port = parsed_url.getPort()
            if port == -1:
                port = 443 if protocol == "https" else 80
            is_https = protocol == "https"
            print("[Auto Login] Parsed URL:")
            print("  Protocol:", protocol)
            print("  Host:", host)
            print("  Port:", port)
            print("  is_https:", is_https)

            # Build the HTTP service
            login_service = self._helpers.buildHttpService(host, port, is_https)
            print("[Auto Login] Built HTTP service")

            # Create the POST request headers
            request_headers = [
                "POST {} HTTP/1.1".format(parsed_url.getPath()),
                "Host: {}".format(host),
                "Content-Type: application/x-www-form-urlencoded",
                "Connection: close"
            ]
            print("[Auto Login] Request headers:")
            for header in request_headers:
                print("  ", header)

            # Build the request body with URL-encoded parameters
            request_body = "username={}&password={}&timestamp={}&checksum={}".format(
                self._helpers.urlEncode(USERNAME),
                self._helpers.urlEncode(PASSWORD),
                self._helpers.urlEncode(timestamp),
                self._helpers.urlEncode(checksum)
            )
            print("[Auto Login] Request body:", request_body)

            # Combine headers and body into a complete HTTP message
            message = self._helpers.buildHttpMessage(request_headers, request_body)
            print("[Auto Login] Built HTTP message")

            # Send the request
            response = self._callbacks.makeHttpRequest(login_service, message)
            print("[Auto Login] Sent HTTP request")

            # Analyze the response to extract cookies
            response_bytes = response.getResponse()
            if response_bytes:
                response_info = self._helpers.analyzeResponse(response_bytes)
                status_code = response_info.getStatusCode()
                print("[Auto Login] Received response with status code:", status_code)

                cookies = response_info.getCookies()
                print("[Auto Login] Extracted cookies:")
                for cookie in cookies:
                    print("  Name:", cookie.getName())
                    print("  Value:", cookie.getValue())
                    print("  Domain:", cookie.getDomain())
                    print("  Path:", cookie.getPath())
                    print("  Expiration:", cookie.getExpiration())
                    print("  -----------------------")

                # Update Burp's cookie jar
                for cookie in cookies:
                    # Ensure the domain is set
                    domain = cookie.getDomain()
                    if domain is None:
                        domain = host  # Set the domain to the request host
                        print("[Auto Login] Cookie domain was None, set to:", domain)

                    # Create a new Cookie object with the domain set
                    new_cookie = Cookie(
                        domain,
                        cookie.getName(),
                        cookie.getValue(),
                        cookie.getPath(),
                        cookie.getExpiration()
                    )
                    print("[Auto Login] Updating cookie:", new_cookie.getName())
                    self._callbacks.updateCookieJar(new_cookie)
                print("[Auto Login] Updated cookie jar successfully")
            else:
                print("[Auto Login] No response received from the server")
        except Exception as e:
            print("[Auto Login] Exception occurred:", str(e))
            import traceback
            traceback.print_exc()

Laajennuksen debuggaaminen

Burp-laajennuksia debugataan muokkaamalla koodia, poistamalla laajennus (unload ruksista), lataamalla se takaisin, kokeilemalla sitä uudestaan, ja tarkkailemalla Output-välilehdeltä lokeja sekä session traceria.

Session tracer on tosiaan melko arvokas työkalu kun luodaan istunnon käsittelytoimintoja. Yritetään nyt lähettää pyyntö.

Hyvä, vaikuttaa toimivan. Katsotaan, mitä istuntojäljittäjä sanoi tästä.

Jep! Voimme nähdä, että istunto tunnistettiin kelvottomaksi, laajennuksemme kutsuttiin ja laajennus päivitti yhden evästeen evästeiden säiliöön.

Harjoitus

Näytä laatikoille, kuka on pomo.

Ticking boxes 3

Tässä labrassa pääset ruksimaan ruutuja!

Tavoite

Ruksi sata ruutua saadaksesi lipun.

Tehtävät

Flag

Löydä lippu (flag) labraympäristöstä ja syötä se alle.

hakatemia pro

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ä.