add stuff
This commit is contained in:
commit
26cd15d899
65 files changed
+4820
No files matched your search
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Serves recon.html to the TV and captures what the TV POSTs back.
|
||||
Run: python3 server.py [port] (default 9000)
|
||||
The TV opens http://<this-pc-ip>:9000/ in its built-in browser.
|
||||
Captures land in ./captures/ as timestamped .txt files.
|
||||
"""
|
||||
import http.server, socketserver, os, sys, datetime, itertools, urllib.parse
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
CAP = os.path.join(HERE, "captures"); os.makedirs(CAP, exist_ok=True)
|
||||
PAGE = os.path.join(HERE, "recon.html")
|
||||
SEQ = itertools.count()
|
||||
|
||||
class H(http.server.BaseHTTPRequestHandler):
|
||||
def log_message(self, *a): pass
|
||||
def _send(self, code, body, ctype="text/html; charset=utf-8"):
|
||||
self.send_response(code)
|
||||
self.send_header("Content-Type", ctype)
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.send_header("Cache-Control", "no-store")
|
||||
self.send_header("Access-Control-Allow-Origin", "*")
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
def do_GET(self):
|
||||
# Live command channel: the TV polls /cmd; the host drops a JS command
|
||||
# into live_cmd.txt. The file is consumed atomically enough for one TV.
|
||||
if self.path.split("?")[0] != "/cmd":
|
||||
print(f"[GET] {self.path} ua={self.headers.get('User-Agent','')[:40]}", flush=True)
|
||||
if self.path.split("?")[0] == "/cmd":
|
||||
cp = os.path.join(HERE, "live_cmd.txt")
|
||||
try:
|
||||
if os.path.exists(cp):
|
||||
with open(cp, "rb") as fh: body = fh.read()
|
||||
os.remove(cp)
|
||||
self._send(200, body, "text/plain; charset=utf-8")
|
||||
else:
|
||||
self._send(204, b"", "text/plain; charset=utf-8")
|
||||
except Exception as e:
|
||||
self._send(500, str(e).encode(), "text/plain; charset=utf-8")
|
||||
return
|
||||
if self.path.startswith("/pub/"):
|
||||
name = os.path.basename(urllib.parse.urlsplit(self.path).path)
|
||||
p = os.path.join(HERE, "pub", name)
|
||||
if os.path.exists(p):
|
||||
with open(p, "rb") as fh:
|
||||
body = fh.read()
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "application/octet-stream")
|
||||
self.send_header("Content-Disposition", 'attachment; filename="%s"' % name)
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.send_header("Cache-Control", "no-store")
|
||||
self.send_header("Access-Control-Allow-Origin", "*")
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
else:
|
||||
self._send(404, b"not found", "text/plain")
|
||||
return
|
||||
routes = {
|
||||
"/": PAGE,
|
||||
"/recon.html": PAGE,
|
||||
"/index.html": PAGE,
|
||||
"/phase2.html": os.path.join(HERE, "phase2.html"),
|
||||
"/phase.js": os.path.join(HERE, "phase.js"),
|
||||
"/icon.png": os.path.join(HERE, "icon.png"),
|
||||
}
|
||||
f = routes.get(self.path.split("?")[0])
|
||||
if f and os.path.exists(f):
|
||||
if f.endswith(".png"): ctype = "image/png"
|
||||
elif f.endswith(".js"): ctype = "application/javascript; charset=utf-8"
|
||||
else: ctype = "text/html; charset=utf-8"
|
||||
with open(f, "rb") as fh: self._send(200, fh.read(), ctype)
|
||||
else:
|
||||
self._send(404, b"not found", "text/plain")
|
||||
def do_POST(self):
|
||||
path = urllib.parse.urlsplit(self.path).path
|
||||
if path == "/dump":
|
||||
q = urllib.parse.parse_qs(urllib.parse.urlsplit(self.path).query)
|
||||
off = int(q.get("off", ["0"])[0])
|
||||
total = int(q.get("total", ["0"])[0])
|
||||
name = os.path.basename(q.get("name", ["libomi.so"])[0]) or "libomi.so"
|
||||
n = int(self.headers.get("Content-Length", 0))
|
||||
body = self.rfile.read(n)
|
||||
d = os.path.join(HERE, ".re", "dumps", "tv")
|
||||
os.makedirs(d, exist_ok=True)
|
||||
p = os.path.join(d, name)
|
||||
with open(p, "wb" if off == 0 else "r+b") as f:
|
||||
f.seek(off)
|
||||
f.write(body)
|
||||
print(f"[+] dump off={off} len={len(body)} total={total}", flush=True)
|
||||
self._send(200, b"ok", "text/plain")
|
||||
return
|
||||
if path != "/recon":
|
||||
self._send(404, b"not found", "text/plain"); return
|
||||
n = int(self.headers.get("Content-Length", 0))
|
||||
body = self.rfile.read(n).decode("utf-8", "replace")
|
||||
ts = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
seq = next(SEQ)
|
||||
p = os.path.join(CAP, f"recon-{ts}-{seq:06d}.txt")
|
||||
with open(p, "w") as f: f.write(body)
|
||||
print(f"[+] captured {len(body)} bytes -> {p}", flush=True)
|
||||
self._send(200, b"ok", "text/plain")
|
||||
|
||||
if __name__ == "__main__":
|
||||
port = int(sys.argv[1]) if len(sys.argv) > 1 else 9000
|
||||
socketserver.TCPServer.allow_reuse_address = True
|
||||
with socketserver.ThreadingTCPServer(("0.0.0.0", port), H) as httpd:
|
||||
print(f"[*] serving recon on http://0.0.0.0:{port}/ (captures -> {CAP})", flush=True)
|
||||
httpd.serve_forever()
|
||||
Reference in new issue
Block a user