Compare commits
4 Commits
9bf1a9a64f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b6b5e1c17c | |||
| 9d16df6053 | |||
| adab1e9746 | |||
| 3af2826c95 |
15
LICENSE
Normal file
15
LICENSE
Normal file
@@ -0,0 +1,15 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2026, acidvegas <acid.vegas@acid.vegas>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
@@ -1,4 +1,4 @@
|
||||
# malserv Test Results
|
||||
# BadTest Results
|
||||
|
||||
Tested against `badtests.py` on port 60666 using **curl 8.5.0**, **urllib.request**, **http.client**, **requests**, and **aiohttp**.
|
||||
|
||||
|
||||
71
badtests.py
71
badtests.py
@@ -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':
|
||||
|
||||
Reference in New Issue
Block a user