initial commit
BIN
assets/images/cat1.jpg
Executable file
|
After Width: | Height: | Size: 18 KiB |
BIN
assets/images/cat2.jpg
Executable file
|
After Width: | Height: | Size: 21 KiB |
BIN
assets/images/logo.jpg
Executable file
|
After Width: | Height: | Size: 24 KiB |
BIN
assets/images/m01-p01.png
Executable file
|
After Width: | Height: | Size: 6.4 MiB |
BIN
assets/images/m01-p02.png
Executable file
|
After Width: | Height: | Size: 6.2 MiB |
BIN
assets/images/m01-p03.png
Executable file
|
After Width: | Height: | Size: 6.4 MiB |
BIN
assets/images/m01-p04.png
Executable file
|
After Width: | Height: | Size: 6.7 MiB |
BIN
assets/images/m01-thumb.png
Executable file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
assets/images/s01-p01.png
Executable file
|
After Width: | Height: | Size: 30 KiB |
BIN
assets/images/s02-p01.png
Executable file
|
After Width: | Height: | Size: 67 KiB |
6
components/footer.html
Executable file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<div class="container">
|
||||||
|
<p class="mono" style="opacity: 0.8;">
|
||||||
|
© 2026 Made with ❤ by t3jfel in Europe.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
13
components/header.html
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<div class="container">
|
||||||
|
<nav>
|
||||||
|
<div class="logo mono">
|
||||||
|
<a href="{{prefix}}index.html">t3jfel<span class="text-gradient">SEC</span></a>
|
||||||
|
</div>
|
||||||
|
<div class="nav-links">
|
||||||
|
<a href="{{prefix}}index.html#about">About</a>
|
||||||
|
<a href="{{prefix}}sec.html">SEC</a>
|
||||||
|
<a href="{{prefix}}magic.html">Magic</a> <a href="{{prefix}}index.html#contact">Contact</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
1097
css/style.css
Executable file
120
index.html
Executable file
|
|
@ -0,0 +1,120 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>t3jfel SEC</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="assets/images/logo.jpg">
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="bg-grid"></div>
|
||||||
|
<div class="bg-orb"></div>
|
||||||
|
|
||||||
|
<header></header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="hero">
|
||||||
|
<div class="container hero-content">
|
||||||
|
<span class="status-badge mono">STATUS: NSA Probably watching.</span>
|
||||||
|
<h1>All Things About <br><span class="text-gradient">Cybersecurity</span></h1>
|
||||||
|
<p class="mono hero-typed"></p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="about">
|
||||||
|
<div class="container">
|
||||||
|
<h2><span class="text-gradient">></span> Operator Profile</h2>
|
||||||
|
<p>I'm a student trying to become Cybersecurity Professional because I'm curious about the "how"
|
||||||
|
and "why".</p>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="skills">
|
||||||
|
<div class="container">
|
||||||
|
<h2><span class="text-gradient">></span> Obsessions</h2>
|
||||||
|
<div class="skills-grid reveal-stagger">
|
||||||
|
<div class="skill-card">
|
||||||
|
<h3 class="mono text-blue">SOC Operations</h3>
|
||||||
|
<p>Splunk SPL, ELK Stack</p>
|
||||||
|
</div>
|
||||||
|
<div class="skill-card">
|
||||||
|
<h3 class="mono text-blue">Digital Forensics</h3>
|
||||||
|
<p>Disk Imaging, Memory Forensics</p>
|
||||||
|
</div>
|
||||||
|
<div class="skill-card">
|
||||||
|
<h3 class="mono text-blue">Malware Analysis</h3>
|
||||||
|
<p>IDA, Ghidra, x64dbg, Static & Dynamic Analysis</p>
|
||||||
|
</div>
|
||||||
|
<div class="skill-card">
|
||||||
|
<h3 class="mono text-blue">0-Day Research</h3>
|
||||||
|
<p>Exploit Development</p>
|
||||||
|
</div>
|
||||||
|
<div class="skill-card">
|
||||||
|
<h3 class="mono text-blue">Scripting</h3>
|
||||||
|
<p>Bash, PowerShell, Python, C/C++.</p>
|
||||||
|
</div>
|
||||||
|
<div class="skill-card">
|
||||||
|
<h3 class="mono text-blue">Blockchain</h3>
|
||||||
|
<p>Crypto Networks && Payments</p>
|
||||||
|
</div>
|
||||||
|
<div class="skill-card">
|
||||||
|
<h3 class="mono text-blue">Electronics</h3>
|
||||||
|
<p>Hardware Hacking && Messing around with hardware in general</p>
|
||||||
|
</div>
|
||||||
|
<div class="skill-card">
|
||||||
|
<h3 class="mono text-blue">Digital World</h3>
|
||||||
|
<p>Security && Privacy, Steam Collector, E-Sports</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="cases">
|
||||||
|
<div class="container">
|
||||||
|
<h2><span class="text-gradient">></span> SEC</h2>
|
||||||
|
<div id="post-container" class="post-list">
|
||||||
|
<p class="mono">Loading...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="contact">
|
||||||
|
<div class="container">
|
||||||
|
<h2><span class="text-gradient">></span> Slide into DM's</h2>
|
||||||
|
<div class="contact-box">
|
||||||
|
<p>Only unserious business!</p>
|
||||||
|
<ul class="mono" style="list-style: none;">
|
||||||
|
<li>E-mail: <a href="mailto:t3jfelsec.catalyst652@passmail.net" class="text-blue">t3jfelsec.catalyst652@passmail.net</a></li>
|
||||||
|
<li>HackTheBox: <a href="https://app.hackthebox.com/users/2301507" target="_blank" class="text-blue">HTB</a></li>
|
||||||
|
<li>pwn.college: <a href="pwn.college" target="_blank" class="text-blue">t3jfel</a></li>
|
||||||
|
<li>LetsDefend: <a href="https://app.letsdefend.io/user/t3jfel" target="_blank" class="text-blue">LD</a></li>
|
||||||
|
<li>Codeberg: <a href="https://code.t3jfel.dev" target="_blank" class="text-blue">Codeberg (Forgejo)</a></li>
|
||||||
|
<li>Matrix Protocol: <a href="https://matrix.to/#/@t3jfel:t3jfel.dev" target="_blank" class="text-blue">Matrix</a></li>
|
||||||
|
<li>Mastodon Social: <a href="https://mastodon.social/@t3jfel" target="_blank" class="text-blue">Mastodon</a></li>
|
||||||
|
<li>In CTF's my name is also: <a target="_blank" class="text-blue">t3jfel</a></li>
|
||||||
|
<li>I also take my steam account and video games seriously: <a href="https://steamcommunity.com/profiles/76561199191057192/" target="_blank" class="text-blue">Steam</a></li>
|
||||||
|
<li>Antique Steam Account: <a href="https://steamcommunity.com/profiles/76561197991879304/" target="_blank" class="text-blue">Steam OLD</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<img src="assets/images/cat2.jpg" alt="cat2">
|
||||||
|
|
||||||
|
<div class="ticker-wrap">
|
||||||
|
<div class="ticker-content" id="skill-issue-feed"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer></footer>
|
||||||
|
|
||||||
|
<script src="js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
276
js/main.js
Executable file
|
|
@ -0,0 +1,276 @@
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
loadComponents().then(() => {
|
||||||
|
initSkillIssueTicker();
|
||||||
|
initCryptoCopier();
|
||||||
|
});
|
||||||
|
|
||||||
|
loadRecentPosts();
|
||||||
|
initTypingEffect();
|
||||||
|
initScrollReveal();
|
||||||
|
initScrollParallax();
|
||||||
|
|
||||||
|
if (document.getElementById('sec-grid')) {
|
||||||
|
initSearchableGrid('sec', 'sec-grid', 'sec-search');
|
||||||
|
}
|
||||||
|
if (document.getElementById('magic-grid')) {
|
||||||
|
initSearchableGrid('magic', 'magic-grid', 'magic-search');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function initScrollParallax() {
|
||||||
|
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||||
|
|
||||||
|
const root = document.documentElement;
|
||||||
|
let scrollY = 0;
|
||||||
|
let ticking = false;
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
root.style.setProperty('--scroll', scrollY);
|
||||||
|
ticking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
scrollY = window.scrollY || window.pageYOffset || 0;
|
||||||
|
if (!ticking) {
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadComponents() {
|
||||||
|
try {
|
||||||
|
const headerRes = await fetch('components/header.html');
|
||||||
|
if (headerRes.ok) {
|
||||||
|
let headerHtml = await headerRes.text();
|
||||||
|
headerHtml = headerHtml.replaceAll('{{prefix}}', '');
|
||||||
|
const headerEl = document.querySelector('header');
|
||||||
|
if (headerEl) headerEl.innerHTML = headerHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
const footerRes = await fetch('components/footer.html');
|
||||||
|
if (footerRes.ok) {
|
||||||
|
const footerEl = document.querySelector('footer');
|
||||||
|
if (footerEl) footerEl.innerHTML = await footerRes.text();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Component load error:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initScrollReveal() {
|
||||||
|
const sections = document.querySelectorAll('main > section');
|
||||||
|
sections.forEach(section => {
|
||||||
|
if (!section.classList.contains('hero')) {
|
||||||
|
section.classList.add('reveal');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
entry.target.classList.add('in-view');
|
||||||
|
observer.unobserve(entry.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { threshold: 0.12, rootMargin: '0px 0px -60px 0px' });
|
||||||
|
|
||||||
|
document.querySelectorAll('.reveal, .reveal-stagger, main > section').forEach(el => {
|
||||||
|
observer.observe(el);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function attachStaggerObserver(root) {
|
||||||
|
if (!root) return;
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
entry.target.classList.add('in-view');
|
||||||
|
observer.unobserve(entry.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' });
|
||||||
|
observer.observe(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTypingEffect() {
|
||||||
|
const el = document.querySelector('.hero-typed');
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
const text = "My Computer Science notes, mostly focused on trying to master cybersecurity.";
|
||||||
|
let index = 0;
|
||||||
|
|
||||||
|
function type() {
|
||||||
|
if (index < text.length) {
|
||||||
|
el.textContent += text[index];
|
||||||
|
index++;
|
||||||
|
setTimeout(type, 28 + Math.random() * 35);
|
||||||
|
} else {
|
||||||
|
el.classList.add('done');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(type, 700);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadRecentPosts() {
|
||||||
|
const container = document.getElementById('post-container');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/collections/cases/records?sort=-created');
|
||||||
|
if (!response.ok) throw new Error('Status: ' + response.status);
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const posts = (data.items || []).slice(0, 6);
|
||||||
|
|
||||||
|
if (posts.length === 0) {
|
||||||
|
container.innerHTML = '<p class="empty-state mono">No posts yet.</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.classList.add('reveal-stagger');
|
||||||
|
container.innerHTML = posts.map(post => {
|
||||||
|
const linkUrl = 'walkthroughs/view.html?id=' + post.id;
|
||||||
|
const tagStyle = post.category === 'magic'
|
||||||
|
? 'color: #a855f7; background: rgba(168, 85, 247, 0.1);'
|
||||||
|
: '';
|
||||||
|
const date = (post.created || '').split(' ')[0];
|
||||||
|
return `
|
||||||
|
<a href="${linkUrl}" class="post-item">
|
||||||
|
<span class="post-meta mono">
|
||||||
|
<span class="post-tag" style="${tagStyle}">[${escapeHtml(post.tag || '')}]</span>
|
||||||
|
${escapeHtml(date)}
|
||||||
|
</span>
|
||||||
|
<h3>${escapeHtml(post.title || '')}</h3>
|
||||||
|
<p>${escapeHtml(post.summary || '')}</p>
|
||||||
|
</a>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
attachStaggerObserver(container);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to sync data:', e);
|
||||||
|
container.innerHTML = '<p class="empty-state mono">Failed to load posts.</p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initSearchableGrid(category, gridId, searchId) {
|
||||||
|
const grid = document.getElementById(gridId);
|
||||||
|
const searchInput = document.getElementById(searchId);
|
||||||
|
if (!grid) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/collections/cases/records?sort=-created');
|
||||||
|
if (!response.ok) throw new Error('Status: ' + response.status);
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const allPosts = data.items || [];
|
||||||
|
const categoryPosts = allPosts.filter(p => p.category === category);
|
||||||
|
|
||||||
|
const render = (items) => {
|
||||||
|
grid.classList.add('reveal-stagger');
|
||||||
|
grid.classList.remove('in-view');
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
grid.classList.remove('reveal-stagger');
|
||||||
|
grid.innerHTML = '<p class="empty-state mono">No results found.</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
grid.innerHTML = items.map(post => {
|
||||||
|
let imageHtml = '';
|
||||||
|
let cardClass = 'sec-card';
|
||||||
|
const date = (post.created || '').split(' ')[0];
|
||||||
|
|
||||||
|
if (category === 'magic' && post.thumbnail) {
|
||||||
|
imageHtml = `<div class="card-img" style="background-image: url('${escapeAttr(post.thumbnail)}');"></div>`;
|
||||||
|
cardClass += ' has-image';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<a href="walkthroughs/view.html?id=${encodeURIComponent(post.id)}" class="${cardClass}">
|
||||||
|
${imageHtml}
|
||||||
|
<div class="card-content">
|
||||||
|
<h3 class="mono">${escapeHtml(post.title || '')}</h3>
|
||||||
|
<span class="card-meta mono">[${escapeHtml(post.tag || '')}] ${escapeHtml(date)}</span>
|
||||||
|
</div>
|
||||||
|
</a>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
requestAnimationFrame(() => grid.classList.add('in-view'));
|
||||||
|
};
|
||||||
|
|
||||||
|
render(categoryPosts);
|
||||||
|
|
||||||
|
if (searchInput) {
|
||||||
|
searchInput.addEventListener('input', (e) => {
|
||||||
|
const term = e.target.value.toLowerCase();
|
||||||
|
const filtered = categoryPosts.filter(post =>
|
||||||
|
(post.title || '').toLowerCase().includes(term) ||
|
||||||
|
(post.tag || '').toLowerCase().includes(term) ||
|
||||||
|
(post.summary || '').toLowerCase().includes(term)
|
||||||
|
);
|
||||||
|
render(filtered);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Grid error:', e);
|
||||||
|
grid.innerHTML = '<p class="empty-state mono">Failed to load.</p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSkillIssueTicker() {
|
||||||
|
const tickerContainer = document.getElementById('skill-issue-feed');
|
||||||
|
if (!tickerContainer) return;
|
||||||
|
|
||||||
|
const messages = [
|
||||||
|
"Zero Days found: 0",
|
||||||
|
"Imposter Syndrome kicking in",
|
||||||
|
"Pigeons are liars",
|
||||||
|
"CERTIFICATIONS: Trust me bro",
|
||||||
|
"The S in IOT stand for Security.",
|
||||||
|
"Must be a layer 8 issue.",
|
||||||
|
"The best way to secure a computer is to turn it off and never power it on.",
|
||||||
|
"And then?"
|
||||||
|
];
|
||||||
|
|
||||||
|
const content = [...messages, ...messages, ...messages]
|
||||||
|
.map(msg => `<span class="ticker-item">${escapeHtml(msg)}</span>`)
|
||||||
|
.join('');
|
||||||
|
tickerContainer.innerHTML = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initCryptoCopier() {
|
||||||
|
document.querySelectorAll('.crypto-addr').forEach(addr => {
|
||||||
|
addr.addEventListener('click', async () => {
|
||||||
|
const originalText = addr.innerText;
|
||||||
|
const cleanAddress = originalText.replace(/\s/g, '');
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(cleanAddress);
|
||||||
|
addr.classList.add('copied');
|
||||||
|
addr.innerText = '[ ADDRESS COPIED ]';
|
||||||
|
setTimeout(() => {
|
||||||
|
addr.classList.remove('copied');
|
||||||
|
addr.innerText = originalText;
|
||||||
|
}, 1500);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Copy failed:', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(str) {
|
||||||
|
if (str == null) return '';
|
||||||
|
return String(str)
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeAttr(str) {
|
||||||
|
if (str == null) return '';
|
||||||
|
return String(str).replace(/"/g, '"').replace(/'/g, ''');
|
||||||
|
}
|
||||||
251
js/viewer.js
Executable file
|
|
@ -0,0 +1,251 @@
|
||||||
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
await loadComponents();
|
||||||
|
initScrollParallax();
|
||||||
|
initLightbox();
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const caseId = urlParams.get('id');
|
||||||
|
|
||||||
|
if (!caseId) {
|
||||||
|
document.getElementById('main-content').innerHTML =
|
||||||
|
'<div class="container"><h2>Error: No case ID specified.</h2></div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadCaseData(caseId);
|
||||||
|
attachLightboxToImages();
|
||||||
|
});
|
||||||
|
|
||||||
|
function initScrollParallax() {
|
||||||
|
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||||
|
const root = document.documentElement;
|
||||||
|
let scrollY = 0;
|
||||||
|
let ticking = false;
|
||||||
|
function update() {
|
||||||
|
root.style.setProperty('--scroll', scrollY);
|
||||||
|
ticking = false;
|
||||||
|
}
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
scrollY = window.scrollY || window.pageYOffset || 0;
|
||||||
|
if (!ticking) {
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadComponents() {
|
||||||
|
try {
|
||||||
|
const headerRes = await fetch('../components/header.html');
|
||||||
|
if (headerRes.ok) {
|
||||||
|
let headerHtml = await headerRes.text();
|
||||||
|
headerHtml = headerHtml.replaceAll('{{prefix}}', '../');
|
||||||
|
document.querySelector('header').innerHTML = headerHtml;
|
||||||
|
}
|
||||||
|
const footerRes = await fetch('../components/footer.html');
|
||||||
|
if (footerRes.ok) {
|
||||||
|
document.querySelector('footer').innerHTML = await footerRes.text();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Component load error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadCaseData(id) {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/collections/cases/records');
|
||||||
|
const data = await response.json();
|
||||||
|
const cases = data.items || [];
|
||||||
|
const currentCase = cases.find(c => c.id === id);
|
||||||
|
|
||||||
|
if (!currentCase) {
|
||||||
|
document.getElementById('main-content').innerHTML =
|
||||||
|
'<div class="container"><h2>Error: Case not found.</h2></div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backLink = document.getElementById('back-link');
|
||||||
|
if (backLink) {
|
||||||
|
if (currentCase.category === 'magic') {
|
||||||
|
backLink.href = '../magic.html';
|
||||||
|
backLink.innerHTML = '← BACK TO MAGIC';
|
||||||
|
} else {
|
||||||
|
backLink.href = '../sec.html';
|
||||||
|
backLink.innerHTML = '← BACK TO SEC';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const titleElement = document.getElementById('case-title');
|
||||||
|
titleElement.innerText = currentCase.title || '';
|
||||||
|
|
||||||
|
if (currentCase.category === 'magic') {
|
||||||
|
titleElement.classList.add('expert-title');
|
||||||
|
titleElement.classList.remove('text-gradient');
|
||||||
|
}
|
||||||
|
|
||||||
|
document.title = (currentCase.title || 'Case Study') + ' | t3jfel SEC';
|
||||||
|
document.getElementById('case-tag').innerText = currentCase.tag || '';
|
||||||
|
const date = (currentCase.created || '').split(' ')[0];
|
||||||
|
document.getElementById('case-meta').innerText =
|
||||||
|
'DATE: ' + date + ' | ID: ' + currentCase.id;
|
||||||
|
document.getElementById('case-intro').innerText = currentCase.intro || '';
|
||||||
|
|
||||||
|
const container = document.getElementById('steps-container');
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
if (currentCase.category === 'magic' && currentCase.sections) {
|
||||||
|
container.className = 'expert-container';
|
||||||
|
currentCase.sections.forEach(section => {
|
||||||
|
let sectionHtml =
|
||||||
|
'<div class="expert-section">' +
|
||||||
|
'<h2 class="expert-h2">' + escapeHtml(section.heading || '') + '</h2>';
|
||||||
|
|
||||||
|
(section.content || []).forEach(block => {
|
||||||
|
if (block.type === 'text') {
|
||||||
|
sectionHtml += '<p class="expert-p">' + escapeHtml(block.value) + '</p>';
|
||||||
|
} else if (block.type === 'code') {
|
||||||
|
const codeVal = Array.isArray(block.value) ? block.value.join('\n') : block.value;
|
||||||
|
sectionHtml +=
|
||||||
|
'<pre class="expert-code-block"><code>' +
|
||||||
|
escapeHtml(codeVal) + '</code></pre>';
|
||||||
|
} else if (block.type === 'image') {
|
||||||
|
const imgPath = block.value.startsWith('http') ? block.value : '../' + block.value;
|
||||||
|
sectionHtml +=
|
||||||
|
'<div class="expert-image-wrapper">' +
|
||||||
|
'<img src="' + escapeAttr(imgPath) + '" alt="Documentation Image" class="lightbox-trigger">' +
|
||||||
|
'</div>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sectionHtml += '</div>';
|
||||||
|
container.insertAdjacentHTML('beforeend', sectionHtml);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
container.className = 'analysis-steps-container';
|
||||||
|
(currentCase.steps || []).forEach((step, idx) => {
|
||||||
|
let bodyHtml = '';
|
||||||
|
if (step.body) {
|
||||||
|
step.body.forEach(block => {
|
||||||
|
if (block.type === 'text') {
|
||||||
|
bodyHtml += '<p>' + escapeHtml(block.value) + '</p>';
|
||||||
|
} else if (block.type === 'code') {
|
||||||
|
const codeVal = Array.isArray(block.value) ? block.value.join('\n') : block.value;
|
||||||
|
bodyHtml += '<pre><code>' + escapeHtml(codeVal) + '</code></pre>';
|
||||||
|
} else if (block.type === 'image') {
|
||||||
|
const imgPath = block.value.startsWith('http') ? block.value : '../' + block.value;
|
||||||
|
bodyHtml += '<img src="' + escapeAttr(imgPath) + '" alt="Step image" class="lightbox-trigger">';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const num = String(idx + 1).padStart(2, '0');
|
||||||
|
const stepHtml =
|
||||||
|
'<article class="blog-step">' +
|
||||||
|
'<div class="step-header">' +
|
||||||
|
'<span class="step-number mono">Step ' + num + '</span>' +
|
||||||
|
'<h2 class="step-title">' + escapeHtml(step.title || '') + '</h2>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="step-body">' + bodyHtml + '</div>' +
|
||||||
|
'</article>';
|
||||||
|
container.insertAdjacentHTML('beforeend', stepHtml);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lb = { scale: 1, tx: 0, ty: 0 };
|
||||||
|
|
||||||
|
function initLightbox() {
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.id = 'lightbox-overlay';
|
||||||
|
overlay.innerHTML =
|
||||||
|
'<div id="lightbox-content">' +
|
||||||
|
'<img id="lightbox-img" src="" alt="Enlarged image">' +
|
||||||
|
'</div>' +
|
||||||
|
'<button id="lightbox-close" aria-label="Close">×</button>';
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
|
||||||
|
const img = document.getElementById('lightbox-img');
|
||||||
|
|
||||||
|
function applyTransform() {
|
||||||
|
img.style.transform =
|
||||||
|
'translate(' + lb.tx + 'px, ' + lb.ty + 'px) scale(' + lb.scale + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
overlay.addEventListener('click', function (e) {
|
||||||
|
if (e.target === overlay || e.target.id === 'lightbox-content') {
|
||||||
|
closeLightbox();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('lightbox-close').addEventListener('click', closeLightbox);
|
||||||
|
|
||||||
|
overlay.addEventListener('wheel', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const rect = img.getBoundingClientRect();
|
||||||
|
const dx = e.clientX - rect.left;
|
||||||
|
const dy = e.clientY - rect.top;
|
||||||
|
|
||||||
|
const factor = e.deltaY < 0 ? 1.12 : 0.9;
|
||||||
|
const newScale = Math.min(Math.max(lb.scale * factor, 0.15), 10);
|
||||||
|
const ratio = newScale / lb.scale;
|
||||||
|
|
||||||
|
lb.tx = lb.tx + dx * (1 - ratio);
|
||||||
|
lb.ty = lb.ty + dy * (1 - ratio);
|
||||||
|
lb.scale = newScale;
|
||||||
|
|
||||||
|
applyTransform();
|
||||||
|
}, { passive: false });
|
||||||
|
|
||||||
|
document.addEventListener('keydown', function (e) {
|
||||||
|
if (overlay.classList.contains('active') && e.key === 'Escape') {
|
||||||
|
closeLightbox();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openLightbox(src) {
|
||||||
|
lb.scale = 1;
|
||||||
|
lb.tx = 0;
|
||||||
|
lb.ty = 0;
|
||||||
|
|
||||||
|
const img = document.getElementById('lightbox-img');
|
||||||
|
img.src = src;
|
||||||
|
img.style.transform = '';
|
||||||
|
|
||||||
|
document.getElementById('lightbox-overlay').classList.add('active');
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeLightbox() {
|
||||||
|
document.getElementById('lightbox-overlay').classList.remove('active');
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function attachLightboxToImages() {
|
||||||
|
document.querySelectorAll('.lightbox-trigger').forEach(function (img) {
|
||||||
|
img.addEventListener('click', function () {
|
||||||
|
openLightbox(img.src);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function escapeHtml(str) {
|
||||||
|
if (str == null) return '';
|
||||||
|
return String(str)
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeAttr(str) {
|
||||||
|
if (str == null) return '';
|
||||||
|
return String(str).replace(/"/g, '"').replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
|
||||||
45
magic.html
Executable file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Ohh Magic! | t3jfel SEC</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="assets/images/logo.jpg">
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="bg-grid"></div>
|
||||||
|
<div class="bg-orb"></div>
|
||||||
|
|
||||||
|
<header></header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="hero archive-hero">
|
||||||
|
<div class="container">
|
||||||
|
<h1><span class="text-gradient">></span> It's Magic</h1>
|
||||||
|
<p class="mono"><b>Deep dives into how the world actually works and how it is getting bypassed or hacked. Maybe some mathematics and physics, cuz I hate it.</b></p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="container">
|
||||||
|
<div class="search-wrapper">
|
||||||
|
<input type="text" id="magic-search" class="search-input" placeholder="Search...">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="magic-grid" class="compact-grid">
|
||||||
|
<p class="mono">Loading...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer></footer>
|
||||||
|
<script src="js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
45
sec.html
Executable file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Security | t3jfel SEC</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="assets/images/logo.jpg">
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="bg-grid"></div>
|
||||||
|
<div class="bg-orb"></div>
|
||||||
|
|
||||||
|
<header></header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="hero archive-hero">
|
||||||
|
<div class="container">
|
||||||
|
<h1><span class="text-gradient">></span> Security Archive</h1>
|
||||||
|
<p class="mono"><b>These might help APT's compromise me, but I always report my findings (if i have :)) first ofc.</b></p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="container">
|
||||||
|
<div class="search-wrapper">
|
||||||
|
<input type="text" id="sec-search" class="search-input" placeholder="Search...">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="sec-grid" class="compact-grid">
|
||||||
|
<p class="mono">Loading...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer></footer>
|
||||||
|
<script src="js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
37
walkthroughs/view.html
Executable file
|
|
@ -0,0 +1,37 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Case Study | Loading...</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="../assets/images/logo.jpg">
|
||||||
|
<link rel="stylesheet" href="../css/style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="bg-grid"></div>
|
||||||
|
<div class="bg-orb"></div>
|
||||||
|
<header></header>
|
||||||
|
<main id="main-content">
|
||||||
|
<section class="hero case-hero">
|
||||||
|
<div class="container case-hero-inner">
|
||||||
|
<a id="back-link" href="../sec.html" class="mono">← BACK TO OPERATIONS</a>
|
||||||
|
<span id="case-tag" class="status-badge mono">LOADING...</span>
|
||||||
|
<h1 id="case-title">Loading...</h1>
|
||||||
|
<p id="case-meta" class="mono">...</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<div class="container">
|
||||||
|
<div id="case-intro"></div>
|
||||||
|
<div id="steps-container" class="analysis-steps-container"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
<footer></footer>
|
||||||
|
<script src="../js/viewer.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||