Original tooling from the Camhak research project (camera teardown of a
rebranded UBIA / Javiscam IP camera). PyQt6 GUI on top of a curses TUI on
top of a service controller; per-service start/stop, intruder detection,
protocol fingerprinting, OAM HMAC signing, CVE verifiers, OTA bucket
probe, firmware fetcher, fuzzer, packet injection.
Tabs: Dashboard, Live Log, Intruders, Cloud API, Fuzzer, Inject, CVEs,
Config, Help. Real-time per-packet protocol detection, conntrack-based
original-destination lookup, log rotation at 1 GiB.
See SECURITY_PAPER.md for the full writeup, site/index.html for the
public report, README.md for usage. Run with:
sudo /usr/bin/python3 gui.py
Co-authored by Setec Labs.
101 lines
3.8 KiB
JavaScript
101 lines
3.8 KiB
JavaScript
/* ─────────────────────────────────────────────────────────
|
|
camhak.seteclabs.io — boot sequence + reveals + hud
|
|
───────────────────────────────────────────────────────── */
|
|
|
|
// ─── HUD live clock ──────────────────────────────────────
|
|
(function clock() {
|
|
const el = document.getElementById("hud-clock");
|
|
if (!el) return;
|
|
function tick() {
|
|
const d = new Date();
|
|
const pad = n => String(n).padStart(2, "0");
|
|
el.textContent = `${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}Z`;
|
|
}
|
|
tick();
|
|
setInterval(tick, 1000);
|
|
})();
|
|
|
|
// ─── Boot sequence (typewriter) ──────────────────────────
|
|
(function boot() {
|
|
const el = document.getElementById("boot");
|
|
if (!el) return;
|
|
|
|
const lines = [
|
|
{ t: "[boot] setec-rom v3.41 init", c: "ok" },
|
|
{ t: "[boot] cpu: armv7l-thumb / 2 cores / 1.0ghz", c: "ok" },
|
|
{ t: "[boot] memory map ........................ ok", c: "ok" },
|
|
{ t: "[boot] crt phosphor warmup .................. ok", c: "ok" },
|
|
{ t: "[net] link: enP4p65s0 / 192.168.1.172", c: "ok" },
|
|
{ t: "[net] upstream: setec.fun / verified", c: "ok" },
|
|
{ t: "[svc] loading payload: camhak/index", c: "" },
|
|
{ t: "[svc] decrypting findings ............... 18 records", c: "ok" },
|
|
{ t: "[warn] subject vendor unresponsive (CISA)", c: "warn" },
|
|
{ t: "[scan] uplink stable. handing off to renderer.", c: "ok" },
|
|
{ t: "", c: "" },
|
|
];
|
|
|
|
let li = 0, ci = 0;
|
|
let buf = "";
|
|
const speed = 6; // ms per char
|
|
|
|
function step() {
|
|
if (li >= lines.length) {
|
|
el.innerHTML = buf + '<span class="cur">█</span>';
|
|
return;
|
|
}
|
|
const line = lines[li];
|
|
if (ci === 0 && line.t.length === 0) {
|
|
buf += "\n";
|
|
li++;
|
|
setTimeout(step, 60);
|
|
return;
|
|
}
|
|
if (ci < line.t.length) {
|
|
ci++;
|
|
} else {
|
|
// line complete; wrap with class
|
|
const wrapped = line.c ? `<span class="${line.c}">${line.t}</span>` : line.t;
|
|
// replace the in-progress text with the wrapped version
|
|
buf = buf.replace(line.t, wrapped) + "\n";
|
|
li++;
|
|
ci = 0;
|
|
setTimeout(step, 90);
|
|
return;
|
|
}
|
|
// render in-progress (without span coloring while typing)
|
|
el.innerHTML = buf + line.t.slice(0, ci) + '<span class="cur">█</span>';
|
|
setTimeout(step, speed + Math.random() * 8);
|
|
}
|
|
|
|
step();
|
|
})();
|
|
|
|
// ─── Section reveal on scroll (one-shot) ─────────────────
|
|
(function reveal() {
|
|
if (!("IntersectionObserver" in window)) {
|
|
document.querySelectorAll(".reveal").forEach(el => el.classList.add("in-view"));
|
|
return;
|
|
}
|
|
const io = new IntersectionObserver((entries) => {
|
|
entries.forEach(e => {
|
|
if (e.isIntersecting) {
|
|
e.target.classList.add("in-view");
|
|
io.unobserve(e.target);
|
|
}
|
|
});
|
|
}, { rootMargin: "0px 0px -10% 0px", threshold: 0.05 });
|
|
|
|
document.querySelectorAll(".reveal").forEach(el => io.observe(el));
|
|
})();
|
|
|
|
// ─── Random subtle glitch on the hero every ~10s ─────────
|
|
(function periodicGlitch() {
|
|
const el = document.getElementById("ascii-logo");
|
|
if (!el) return;
|
|
setInterval(() => {
|
|
el.style.animation = "none";
|
|
void el.offsetWidth;
|
|
el.style.animation = "glitch-shake 0.4s linear";
|
|
}, 9000 + Math.random() * 5000);
|
|
})();
|